• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package com.android.codegen
2 
3 import com.github.javaparser.JavaParser
4 import com.github.javaparser.ast.body.FieldDeclaration
5 import com.github.javaparser.ast.expr.ClassExpr
6 import com.github.javaparser.ast.expr.Name
7 import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
8 import com.github.javaparser.ast.expr.StringLiteralExpr
9 import com.github.javaparser.ast.type.ArrayType
10 import com.github.javaparser.ast.type.ClassOrInterfaceType
11 import com.github.javaparser.javadoc.Javadoc
12 
13 data class FieldInfo(
14     val index: Int,
15     val fieldAst: FieldDeclaration,
16     private val classInfo: ClassInfo
17 ) {
18 
19     val classPrinter = classInfo as ClassPrinter
20 
21     // AST
22     internal val variableAst = fieldAst.variables[0]
23     val typeAst = variableAst.type
24 
25     // Field type
26     val Type = typeAst.asString()
27     val FieldClass = Type.takeWhile { it != '<' }
28     val isPrimitive = Type in PRIMITIVE_TYPES
29 
30     // Javadoc
31     val javadoc: Javadoc? = fieldAst.javadoc.orElse(null)
32     private val javadocText = javadoc?.toText()?.let {
33         // Workaround for a bug in Javaparser for javadocs starting with {
34         if (it.hasUnbalancedCurlyBrace()) "{$it" else it
35     }
36     val javadocTextNoAnnotationLines = javadocText
37             ?.lines()
38             ?.dropLastWhile { it.startsWith("@") || it.isBlank() }
39             ?.let { if (it.isEmpty()) null else it }
40     val javadocFull = javadocText
41             ?.trimBlankLines()
42             ?.mapLines { " * $this" }
43             ?.let { "/**\n$it\n */" }
44 
45 
46     // Field name
47     val name = variableAst.name.asString()!!
48     private val isNameHungarian = name[0] == 'm' && name[1].isUpperCase()
49     val NameUpperCamel = if (isNameHungarian) name.substring(1) else name.capitalize()
50     val nameLowerCamel = if (isNameHungarian) NameUpperCamel.decapitalize() else name
51     val _name = if (name != nameLowerCamel) nameLowerCamel else "_$nameLowerCamel"
52     val SingularNameOrNull by lazy {
53         classPrinter {
54             fieldAst.annotations
55                     .find { it.nameAsString == PluralOf }
56                     ?.let { it as? SingleMemberAnnotationExpr }
57                     ?.memberValue
58                     ?.let { it as? StringLiteralExpr }
59                     ?.value
60                     ?.toLowerCamel()
61                     ?.capitalize()
62         }
63     }
64     val SingularName by lazy { SingularNameOrNull ?: NameUpperCamel }
65 
66 
67     // Field value
68     val mayBeNull: Boolean
69         get() = when {
70             isPrimitive -> false
71             "@${classPrinter.NonNull}" in annotations -> false
72             "@${classPrinter.NonEmpty}" in annotations -> false
73             isNullable -> true
74             lazyInitializer != null -> true
75             else -> classPrinter { !FeatureFlag.IMPLICIT_NONNULL() }
76         }
77     val lazyInitializer
78         get() = classInfo.classAst.methods.find { method ->
79             method.nameAsString == "lazyInit$NameUpperCamel" && method.parameters.isEmpty()
80         }?.nameAsString
81     val internalGetter get() = if (lazyInitializer != null) "get$NameUpperCamel()" else name
82     val defaultExpr: Any?
83         get() {
84             variableAst.initializer.orElse(null)?.let { return it }
85             classInfo.classAst.methods.find {
86                 it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty()
87             }?.run { return "$nameAsString()" }
88             return null
89         }
90     val hasDefault get() = defaultExpr != null
91 
92 
93     // Generic args
94     val isArray = Type.endsWith("[]")
95     val isList = FieldClass == "List" || FieldClass == "ArrayList"
96     val isMap = FieldClass == "Map" || FieldClass == "ArrayMap"
97             || FieldClass == "HashMap" || FieldClass == "LinkedHashMap"
98     val fieldBit = bitAtExpr(index)
99     var isLast = false
100     val isFinal = fieldAst.isFinal
101     val fieldTypeGenegicArgs = when (typeAst) {
102         is ArrayType -> listOf(fieldAst.elementType.asString())
103         is ClassOrInterfaceType -> {
104             typeAst.typeArguments.orElse(null)?.map { it.asString() } ?: emptyList()
105         }
106         else -> emptyList()
107     }
108     val FieldInnerType = fieldTypeGenegicArgs.firstOrNull()
109     val FieldInnerClass = FieldInnerType?.takeWhile { it != '<' }
110 
111 
112     // Annotations
113     var intOrStringDef = null as ConstDef?
114     val annotations by lazy {
115         if (FieldClass in BUILTIN_SPECIAL_PARCELLINGS) {
116             classPrinter {
117                 fileInfo.apply {
118                     fieldAst.addAnnotation(SingleMemberAnnotationExpr(
119                             Name(ParcelWith),
120                             ClassExpr(parseJava(JavaParser::parseClassOrInterfaceType,
121                                     "$Parcelling.BuiltIn.For$FieldClass"))))
122                 }
123             }
124         }
125         fieldAst.annotations.map { it.removeComment().toString() }
126     }
127     val annotationsNoInternal by lazy {
128         annotations.filterNot { ann ->
129             classPrinter {
130                 internalAnnotations.any {
131                     it in ann
132                 }
133             }
134         }
135     }
136 
137     fun hasAnnotation(a: String) = annotations.any { it.startsWith(a) }
138     val isNullable by lazy { hasAnnotation("@Nullable") }
139     val isNonEmpty by lazy { hasAnnotation("@${classPrinter.NonEmpty}") }
140     val customParcellingClass by lazy {
141         fieldAst.annotations.find { it.nameAsString == classPrinter.ParcelWith }
142                 ?.singleArgAs<ClassExpr>()
143                 ?.type
144                 ?.asString()
145     }
146     val annotationsAndType by lazy { (annotationsNoInternal + Type).joinToString(" ") }
147     val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } }
148 
149     val SetterParamType = if (isArray) "$FieldInnerType..." else Type
150     val annotationsForSetterParam by lazy {
151         buildList<String> {
152             addAll(annotationsNoInternal)
153             classPrinter {
154                 if ("@$Nullable" in annotations
155                         && "@$MaySetToNull" !in annotations) {
156                     remove("@$Nullable")
157                     add("@$NonNull")
158                 }
159             }
160         }.joinToString(" ")
161     }
162     val annotatedTypeForSetterParam by lazy { "$annotationsForSetterParam $SetterParamType" }
163 
164     // Utilities
165 
166     /**
167      * `mFoo.size()`
168      */
169     val ClassPrinter.sizeExpr get() = when {
170         isArray && FieldInnerClass !in PRIMITIVE_TYPES ->
171             memberRef("com.android.internal.util.ArrayUtils.size") + "($name)"
172         isArray -> "$name.length"
173         listOf("List", "Set", "Map").any { FieldClass.endsWith(it) } ->
174             memberRef("com.android.internal.util.CollectionUtils.size") + "($name)"
175         Type == "String" -> memberRef("android.text.TextUtils.length") + "($name)"
176         Type == "CharSequence" -> "$name.length()"
177         else -> "$name.size()"
178     }
179     /**
180      * `mFoo.get(0)`
181      */
182     fun elemAtIndexExpr(indexExpr: String) = when {
183         isArray -> "$name[$indexExpr]"
184         FieldClass == "ArraySet" -> "$name.valueAt($indexExpr)"
185         else -> "$name.get($indexExpr)"
186     }
187     /**
188      * `mFoo.isEmpty()`
189      */
190     val ClassPrinter.isEmptyExpr get() = when {
191         isArray || Type == "CharSequence" -> "$sizeExpr == 0"
192         else -> "$name.isEmpty()"
193     }
194 
195     /**
196      * `mFoo == that` or `Objects.equals(mFoo, that)`, etc.
197      */
198     fun ClassPrinter.isEqualToExpr(that: String) = when {
199         Type in PRIMITIVE_TYPES -> "$internalGetter == $that"
200         isArray -> "${memberRef("java.util.Arrays.equals")}($internalGetter, $that)"
201         else -> "${memberRef("java.util.Objects.equals")}($internalGetter, $that)"
202     }
203 
204     /**
205      * Parcel.write* and Parcel.read* method name wildcard values
206      */
207     val ParcelMethodsSuffix = when {
208         FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
209                 listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
210                         "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
211             FieldClass
212         isMap && fieldTypeGenegicArgs[0] == "String" -> "Map"
213         isArray -> when {
214             FieldInnerType!! in (PRIMITIVE_TYPES + "String") -> FieldInnerType + "Array"
215             isBinder(FieldInnerType) -> "BinderArray"
216             else -> "TypedArray"
217         }
218         isList -> when {
219             FieldInnerType == "String" -> "StringList"
220             isBinder(FieldInnerType!!) -> "BinderList"
221             else -> "ParcelableList"
222         }
223         isStrongBinder(Type) -> "StrongBinder"
224         isIInterface(Type) -> "StrongInterface"
225         else -> "TypedObject"
226     }.capitalize()
227 
228     private fun isStrongBinder(type: String) = type == "Binder" || type == "IBinder"
229     private fun isBinder(type: String) = type == "Binder" || type == "IBinder" || isIInterface(type)
230     private fun isIInterface(type: String) = type.length >= 2 && type[0] == 'I' && type[1].isUpperCase()
231 }