<lambda>null1 package com.android.codegen
2 
3 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
4 import com.github.javaparser.ast.expr.*
5 import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations
6 import com.github.javaparser.ast.type.ClassOrInterfaceType
7 import com.github.javaparser.ast.type.Type
8 
9 
10 fun ClassPrinter.getInputSignatures(): List<String> {
11     return generateInputSignaturesForClass(classAst) +
12             annotationToString(classAst.annotations.find { it.nameAsString == DataClass }) +
13             generateInputSignaturesForClass(customBaseBuilderAst)
14 }
15 
ClassPrinternull16 private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterfaceDeclaration?): List<String> {
17     if (classAst == null) return emptyList()
18 
19     return classAst.fields.map { fieldAst ->
20         buildString {
21             append(fieldAst.modifiers.joinToString(" ") { it.keyword.asString() })
22             append(" ")
23             append(annotationsToString(fieldAst))
24             append(" ")
25             append(getFullClassName(fieldAst.commonType))
26             append(" ")
27             append(fieldAst.variables.joinToString(", ") { it.nameAsString })
28         }
29     } + classAst.methods.map { methodAst ->
30         buildString {
31             append(methodAst.modifiers.joinToString(" ") { it.keyword.asString() })
32             append(" ")
33             append(annotationsToString(methodAst))
34             append(" ")
35             append(getFullClassName(methodAst.type))
36             append(" ")
37             append(methodAst.nameAsString)
38             append("(")
39             append(methodAst.parameters.joinToString(",") { getFullClassName(it.type) })
40             append(")")
41         }
42     } + ("class ${classAst.nameAsString}" +
43             " extends ${classAst.extendedTypes.map { getFullClassName(it) }.ifEmpty { listOf("java.lang.Object") }.joinToString(", ")}" +
44             " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]") +
45     classAst.nestedNonDataClasses.flatMap { nestedClass ->
46         generateInputSignaturesForClass(nestedClass)
47     }
48 }
49 
annotationsToStringnull50 private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String {
51     return annotatedAst
52             .annotations
53             .groupBy { it.nameAsString } // dedupe annotations by name (javaparser bug?)
54             .values
55             .joinToString(" ") {
56                 annotationToString(it[0])
57             }
58 }
59 
ClassPrinternull60 private fun ClassPrinter.annotationToString(ann: AnnotationExpr?): String {
61     if (ann == null) return ""
62     return buildString {
63         append("@")
64         append(getFullClassName(ann.nameAsString))
65         if (ann is MarkerAnnotationExpr) return@buildString
66         if (!ann.nameAsString.startsWith("DataClass")) return@buildString
67 
68         append("(")
69 
70         when (ann) {
71             is SingleMemberAnnotationExpr -> {
72                 appendExpr(this, ann.memberValue)
73             }
74             is NormalAnnotationExpr -> {
75                 ann.pairs.forEachLastAware { pair, isLast ->
76                     append(pair.nameAsString)
77                     append("=")
78                     appendExpr(this, pair.value)
79                     if (!isLast) append(", ")
80                 }
81             }
82         }
83 
84         append(")")
85     }.replace("\"", "\\\"")
86 }
87 
ClassPrinternull88 private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) {
89     when (ex) {
90         is ClassExpr -> sb.append(getFullClassName(ex.typeAsString)).append(".class")
91         is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
92         is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
93         is DoubleLiteralExpr -> sb.append(ex.asDouble())
94         is ArrayInitializerExpr -> {
95             sb.append("{")
96             ex.values.forEachLastAware { arrayElem, isLast ->
97                 appendExpr(sb, arrayElem)
98                 if (!isLast) sb.append(", ")
99             }
100             sb.append("}")
101         }
102         else -> sb.append(ex)
103     }
104 }
105 
getFullClassNamenull106 private fun ClassPrinter.getFullClassName(type: Type): String {
107     return if (type is ClassOrInterfaceType) {
108 
109         getFullClassName(buildString {
110             type.scope.ifPresent { append(it).append(".") }
111             append(type.nameAsString)
112         }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(",") {getFullClassName(it)}}?.let { "<$it>" } ?: "")
113     } else getFullClassName(type.asString())
114 }
115 
getFullClassNamenull116 private fun ClassPrinter.getFullClassName(className: String): String {
117     if (className.endsWith("[]")) return getFullClassName(className.removeSuffix("[]")) + "[]"
118 
119     if (className.matches("\\.[a-z]".toRegex())) return className //qualified name
120 
121     if ("." in className) return getFullClassName(className.substringBeforeLast(".")) + "." + className.substringAfterLast(".")
122 
123     fileAst.imports.find { imp ->
124         imp.nameAsString.endsWith(".$className")
125     }?.nameAsString?.let { return it }
126 
127     val thisPackagePrefix = fileAst.packageDeclaration.map { it.nameAsString + "." }.orElse("")
128     val thisClassPrefix = thisPackagePrefix + classAst.nameAsString + "."
129 
130     if (classAst.nameAsString == className) return thisPackagePrefix + classAst.nameAsString
131 
132     nestedTypes.find {
133         it.nameAsString == className
134     }?.let { return thisClassPrefix + it.nameAsString }
135 
136     if (className == CANONICAL_BUILDER_CLASS || className == BASE_BUILDER_CLASS) {
137         return thisClassPrefix + className
138     }
139 
140     constDefs.find { it.AnnotationName == className }?.let { return thisClassPrefix + className }
141 
142     if (tryOrNull { Class.forName("java.lang.$className") } != null) {
143         return "java.lang.$className"
144     }
145 
146     if (className[0].isLowerCase()) return className //primitive
147 
148     if (className[0] == '?') return className //wildcard
149 
150     return thisPackagePrefix + className
151 }
152 
tryOrNullnull153 private inline fun <T> tryOrNull(f: () -> T?) = try {
154     f()
155 } catch (e: Exception) {
156     null
157 }
158