Skip to content

Instantly share code, notes, and snippets.

@DjakaTechnology
Last active August 1, 2022 06:14
Show Gist options
  • Save DjakaTechnology/84620de7c7f6d908849afc4f0afc63f9 to your computer and use it in GitHub Desktop.
Save DjakaTechnology/84620de7c7f6d908849afc4f0afc63f9 to your computer and use it in GitHub Desktop.
This will migrate file from gson or regular POJO to moshi
#!/usr/bin/env kotlin
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
/**
* How to use:
* Run script
* Follow the instruction
* */
enum class MigrationType(val id: Int, val message: String) {
ONLY_WITHOUT_MOSHI(1, "Migrate files that doesn't contain moshi import"),
ONLY_GSON_IMPORT(2, "Migrate files that contain gson import"),
ALL_FILE(3, "Migrate all files"),
}
enum class ResultType(val id: Int, val message: String) {
OUTPUT_SUBFOLDER(1, """Create a new file inside "output/" subfolder"""),
DIRECT(2, "Write file directly")
}
enum class FileExtension(val id: Int, val message: String) {
KOTLIN(1, "Only migrate kotlin files"),
JAVA(2, "Only migrate Java files"),
ALL(3, "Migrate both Kotlin and Java")
}
println(
"""
=================
Specify target path.
- Path can be relative path from this file
- Path can be absoulte path
=================
""".trimIndent()
)
val targetPath = readLine() ?: throw IllegalStateException("Input folder cannot be null")
println(
"""
=================
Select migrating type:
${MigrationType.values().joinToString("\n") { "${it.id} ${it.message}" }}
=================""".trimIndent()
)
val migrationType = readLine()?.toInt()?.let { input ->
MigrationType.values().first { it.id == input }
} ?: throw IllegalStateException("Please choose between 1, 2 or 3")
println(
"""
=================
Select affected file extension:
${FileExtension.values().joinToString("\n") { "${it.id} ${it.message}" }}
=================""".trimIndent()
)
val fileExtensionType = readLine()?.toInt()?.let { input ->
FileExtension.values().first { it.id == input }
} ?: throw IllegalStateException("Please choose between 1 & 2")
println(
"""
=================
Select result type:
${ResultType.values().joinToString("\n") { "${it.id} ${it.message}" }}
=================""".trimIndent()
)
val resultType = readLine()?.toInt()?.let { input ->
ResultType.values().first { it.id == input }
} ?: throw IllegalStateException("Please choose between 1 & 2")
println(
"""
=================
Running operation with:
Path: ${targetPath}
Migration: ${migrationType.message}
Result: ${resultType.message}
=================
""".trimIndent()
)
println()
val currentPath: Path = Paths.get("").toAbsolutePath()
val inputPath: Path = currentPath.resolve(targetPath)
var isKotlinAllowed = true
var isJavaAllowed = true
when(fileExtensionType) {
FileExtension.KOTLIN -> {
isKotlinAllowed = true
isJavaAllowed = false
}
FileExtension.JAVA -> {
isKotlinAllowed = false
isJavaAllowed = false
}
FileExtension.ALL -> {
isKotlinAllowed = true
isJavaAllowed = true
}
}
Files.walk(inputPath).forEach {
val isKotlin = it.fileName.toString().endsWith(".kt")
val isJava = it.fileName.toString().endsWith(".java")
if ((isKotlin && isKotlinAllowed) || (isJava && isJavaAllowed)) {
println("Found: ${it.toAbsolutePath()}")
val content = it.toFile().readText()
val result = when (migrationType) {
MigrationType.ONLY_GSON_IMPORT -> if (isHadGson(content)) injectMoshi(content, isJava) else content
MigrationType.ONLY_WITHOUT_MOSHI -> if (isHadMoshi(content)) content else injectMoshi(content, isJava)
MigrationType.ALL_FILE -> injectMoshi(content, isJava)
}
when (resultType) {
ResultType.OUTPUT_SUBFOLDER -> {
val relativePath = inputPath.relativize(it)
if (relativePath.parent != null) {
File("output/${relativePath.parent}").mkdirs()
} else {
File("output").mkdirs()
}
File("output/${relativePath}").apply {
println("Writing to: $absolutePath")
writeText(result)
}
}
ResultType.DIRECT -> {
it.toFile().apply {
println("Writing to: $absolutePath")
writeText(result)
}
}
}
}
}
fun injectMoshi(
content: String,
semiColon: Boolean = false,
): String {
val import = mutableSetOf<String>()
val result = mutableListOf<String>()
val importKey = "import "
val moshiJsonClass = "com.squareup.moshi.JsonClass"
val moshiJson = "com.squareup.moshi.Json"
val tempAnnotation = mutableListOf<String>()
content.lines().forEach {
when {
it.startsWith(importKey) -> {
import.add(it.substring(importKey.length))
}
it.contains("@") -> {
if (isInsideComment(it)) {
result.add(it)
return@forEach
}
tempAnnotation.add(it)
result.add(it)
}
it.contains("fun ") -> {
if (isInsideComment(it)) {
result.add(it)
return@forEach
}
result.add(it)
tempAnnotation.clear()
}
it.contains("class ") -> {
if (isInsideComment(it)) {
result.add(it)
return@forEach
}
if (it.contains("enum class ")) {
result.add(it)
return@forEach
}
val jsonClassAdapterString = "@JsonClass(generateAdapter = true)"
if (tempAnnotation.isNotEmpty()) {
val isHadJsonClassAnnotation = tempAnnotation.firstOrNull { it.contains(jsonClassAdapterString) } != null
if (isHadJsonClassAnnotation) {
result.add(it)
tempAnnotation.clear()
return@forEach
}
}
// Add whitespace with import when needed
if (import.isEmpty()) {
result.add("")
}
import.add(moshiJsonClass)
val indentSize: Int = it.indexOfFirst { it.isLetter() }
val indent = " ".repeat(indentSize)
result.add("$indent$jsonClassAdapterString")
result.add(it)
}
it.contains("@SerializedName(") -> {
import.add(moshiJson)
val indentSize = it.indexOf("@SerializedName(")
val indent = " ".repeat(indentSize)
val fieldName = it.trim()
.substringAfter("@SerializedName(\"")
.substringBefore("\")")
result.add("""$indent@Json(name = "$fieldName")""")
result.add(it)
}
else -> {
result.add(it)
}
}
}
/**
* Reinsert import back to result
*
* index = 0 - 1 is reserved for:
* =============
* package xxxx
*
* =============
* */
if (result.size != 2) {
val importString = import.toList().sorted().joinToString("\n") {
if (semiColon) {
"$importKey$it;"
} else {
importKey + it
}
}
if (importString.isNotEmpty()) {
result.add(2, importString)
}
}
return result.joinToString("\n")
}
fun isHadGson(content: String): Boolean {
return content.contains("com.google.gson")
}
fun isHadMoshi(content: String): Boolean {
return content.contains("com.squareup.moshi.")
}
fun isInsideComment(it: String) = it.startsWith("//") || it.startsWith("*") || it.startsWith(" *") || it.startsWith("/**")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment