• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.model.psi
18 
19 import com.android.SdkConstants
20 import com.android.tools.lint.detector.api.ConstantEvaluator
21 import com.android.tools.metalava.model.AnnotationArrayAttributeValue
22 import com.android.tools.metalava.model.AnnotationAttribute
23 import com.android.tools.metalava.model.AnnotationAttributeValue
24 import com.android.tools.metalava.model.AnnotationItem
25 import com.android.tools.metalava.model.AnnotationSingleAttributeValue
26 import com.android.tools.metalava.model.AnnotationTarget
27 import com.android.tools.metalava.model.ClassItem
28 import com.android.tools.metalava.model.DefaultAnnotationItem
29 import com.android.tools.metalava.model.Item
30 import com.intellij.psi.PsiClass
31 import com.intellij.psi.PsiExpression
32 import com.intellij.psi.PsiField
33 import com.intellij.psi.PsiLiteral
34 import com.intellij.psi.PsiMethod
35 import com.intellij.psi.impl.JavaConstantExpressionEvaluator
36 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
37 import org.jetbrains.uast.UAnnotation
38 import org.jetbrains.uast.UBinaryExpression
39 import org.jetbrains.uast.UCallExpression
40 import org.jetbrains.uast.UElement
41 import org.jetbrains.uast.UExpression
42 import org.jetbrains.uast.ULiteralExpression
43 import org.jetbrains.uast.UReferenceExpression
44 import org.jetbrains.uast.util.isArrayInitializer
45 
46 class UAnnotationItem private constructor(
47     override val codebase: PsiBasedCodebase,
48     val uAnnotation: UAnnotation,
49     private val originalName: String?
50 ) : DefaultAnnotationItem(codebase) {
51     private val qualifiedName = AnnotationItem.mapName(codebase, originalName)
52 
53     private var attributes: List<AnnotationAttribute>? = null
54 
originalNamenull55     override fun originalName(): String? = originalName
56 
57     override fun toString(): String = toSource()
58 
59     override fun toSource(target: AnnotationTarget): String {
60         val sb = StringBuilder(60)
61         appendAnnotation(codebase, sb, uAnnotation, originalName, target)
62         return sb.toString()
63     }
64 
resolvenull65     override fun resolve(): ClassItem? {
66         return codebase.findClass(originalName ?: return null)
67     }
68 
isNonNullnull69     override fun isNonNull(): Boolean {
70         if (uAnnotation.javaPsi is KtLightNullabilityAnnotation &&
71             originalName == ""
72         ) {
73             // Hack/workaround: some UAST annotation nodes do not provide qualified name :=(
74             return true
75         }
76         return super.isNonNull()
77     }
78 
qualifiedNamenull79     override fun qualifiedName() = qualifiedName
80 
81     override fun attributes(): List<AnnotationAttribute> {
82         if (attributes == null) {
83             val uAttributes = uAnnotation.attributeValues
84             attributes = if (uAttributes.isEmpty()) {
85                 emptyList()
86             } else {
87                 val list = mutableListOf<AnnotationAttribute>()
88                 for (parameter in uAttributes) {
89                     list.add(
90                         UAnnotationAttribute(
91                             codebase,
92                             parameter.name ?: SdkConstants.ATTR_VALUE, parameter.expression
93                         )
94                     )
95                 }
96                 list
97             }
98         }
99 
100         return attributes!!
101     }
102 
103     companion object {
createnull104         fun create(codebase: PsiBasedCodebase, uAnnotation: UAnnotation, qualifiedName: String? = uAnnotation.qualifiedName): UAnnotationItem {
105             return UAnnotationItem(codebase, uAnnotation, qualifiedName)
106         }
107 
createnull108         fun create(codebase: PsiBasedCodebase, original: UAnnotationItem): UAnnotationItem {
109             return UAnnotationItem(codebase, original.uAnnotation, original.originalName)
110         }
111 
appendAnnotationnull112         private fun appendAnnotation(
113             codebase: PsiBasedCodebase,
114             sb: StringBuilder,
115             uAnnotation: UAnnotation,
116             originalName: String?,
117             target: AnnotationTarget
118         ) {
119             val qualifiedName = AnnotationItem.mapName(codebase, originalName, null, target) ?: return
120 
121             val attributes = uAnnotation.attributeValues
122             if (attributes.isEmpty()) {
123                 sb.append("@$qualifiedName")
124                 return
125             }
126 
127             sb.append("@")
128             sb.append(qualifiedName)
129             sb.append("(")
130             if (attributes.size == 1 && (attributes[0].name == null || attributes[0].name == SdkConstants.ATTR_VALUE)) {
131                 // Special case: omit "value" if it's the only attribute
132                 appendValue(codebase, sb, attributes[0].expression, target)
133             } else {
134                 var first = true
135                 for (attribute in attributes) {
136                     if (first) {
137                         first = false
138                     } else {
139                         sb.append(", ")
140                     }
141                     sb.append(attribute.name ?: SdkConstants.ATTR_VALUE)
142                     sb.append('=')
143                     appendValue(codebase, sb, attribute.expression, target)
144                 }
145             }
146             sb.append(")")
147         }
148 
appendValuenull149         private fun appendValue(
150             codebase: PsiBasedCodebase,
151             sb: StringBuilder,
152             value: UExpression?,
153             target: AnnotationTarget
154         ) {
155             // Compute annotation string -- we don't just use value.text here
156             // because that may not use fully qualified names, e.g. the source may say
157             //  @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
158             // and we want to compute
159             //  @android.support.annotation.RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
160             when (value) {
161                 null -> sb.append("null")
162                 is ULiteralExpression -> sb.append(CodePrinter.constantToSource(value.value))
163                 is UReferenceExpression -> {
164                     val resolved = value.resolve()
165                     when (resolved) {
166                         is PsiField -> {
167                             val containing = resolved.containingClass
168                             if (containing != null) {
169                                 // If it's a field reference, see if it looks like the field is hidden; if
170                                 // so, inline the value
171                                 val cls = codebase.findOrCreateClass(containing)
172                                 val initializer = resolved.initializer
173                                 if (initializer != null) {
174                                     val fieldItem = cls.findField(resolved.name)
175                                     if (fieldItem == null || fieldItem.isHiddenOrRemoved()) {
176                                         // Use the literal value instead
177                                         val source = getConstantSource(initializer)
178                                         if (source != null) {
179                                             sb.append(source)
180                                             return
181                                         }
182                                     }
183                                 }
184                                 containing.qualifiedName?.let {
185                                     sb.append(it).append('.')
186                                 }
187                             }
188 
189                             sb.append(resolved.name)
190                         }
191                         is PsiClass -> resolved.qualifiedName?.let { sb.append(it) }
192                         else -> {
193                             sb.append(value.sourcePsi?.text ?: value.asSourceString())
194                         }
195                     }
196                 }
197                 is UBinaryExpression -> {
198                     appendValue(codebase, sb, value.leftOperand, target)
199                     sb.append(' ')
200                     sb.append(value.operator.text)
201                     sb.append(' ')
202                     appendValue(codebase, sb, value.rightOperand, target)
203                 }
204                 is UCallExpression -> {
205                     if (value.isArrayInitializer()) {
206                         sb.append('{')
207                         var first = true
208                         for (initializer in value.valueArguments) {
209                             if (first) {
210                                 first = false
211                             } else {
212                                 sb.append(", ")
213                             }
214                             appendValue(codebase, sb, initializer, target)
215                         }
216                         sb.append('}')
217                     } else {
218                         println("todo")
219                     }
220                 }
221                 is UAnnotation -> {
222                     appendAnnotation(codebase, sb, value, value.qualifiedName, target)
223                 }
224                 else -> {
225                     val source = getConstantSource(value)
226                     if (source != null) {
227                         sb.append(source)
228                         return
229                     }
230                     sb.append(value.sourcePsi?.text ?: value.asSourceString())
231                 }
232             }
233         }
234 
getConstantSourcenull235         private fun getConstantSource(value: UExpression): String? {
236             val constant = value.evaluate()
237             return CodePrinter.constantToExpression(constant)
238         }
239 
getConstantSourcenull240         private fun getConstantSource(value: PsiExpression): String? {
241             val constant = JavaConstantExpressionEvaluator.computeConstantExpression(value, false)
242             return CodePrinter.constantToExpression(constant)
243         }
244     }
245 }
246 
247 class UAnnotationAttribute(
248     codebase: PsiBasedCodebase,
249     override val name: String,
250     psiValue: UExpression
251 ) : AnnotationAttribute {
252     override val value: AnnotationAttributeValue = UAnnotationValue.create(
253         codebase, psiValue
254     )
255 }
256 
257 abstract class UAnnotationValue : AnnotationAttributeValue {
258     companion object {
createnull259         fun create(codebase: PsiBasedCodebase, value: UExpression): UAnnotationValue {
260             return if (value.isArrayInitializer()) {
261                 UAnnotationArrayAttributeValue(codebase, value as UCallExpression)
262             } else {
263                 UAnnotationSingleAttributeValue(codebase, value)
264             }
265         }
266     }
267 
toStringnull268     override fun toString(): String = toSource()
269 }
270 
271 class UAnnotationSingleAttributeValue(
272     private val codebase: PsiBasedCodebase,
273     private val psiValue: UExpression
274 ) : UAnnotationValue(), AnnotationSingleAttributeValue {
275     override val valueSource: String = getText(psiValue)
276     override val value: Any?
277         get() {
278             if (psiValue is ULiteralExpression) {
279                 val value = psiValue.value
280                 if (value != null) {
281                     return value
282                 } else if (psiValue.isNull) {
283                     return null
284                 }
285             }
286             if (psiValue is PsiLiteral) {
287                 return psiValue.value ?: getText(psiValue).removeSurrounding("\"")
288             }
289 
290             val value = ConstantEvaluator.evaluate(null, psiValue)
291             if (value != null) {
292                 return value
293             }
294 
295             return getText(psiValue).removeSurrounding("\"")
296         }
297 
298     override fun value(): Any? = value
299 
300     override fun toSource(): String = getText(psiValue)
301 
302     override fun resolve(): Item? {
303         if (psiValue is UReferenceExpression) {
304             val resolved = psiValue.resolve()
305             when (resolved) {
306                 is PsiField -> return codebase.findField(resolved)
307                 is PsiClass -> return codebase.findOrCreateClass(resolved)
308                 is PsiMethod -> return codebase.findMethod(resolved)
309             }
310         }
311         return null
312     }
313 }
314 
315 class UAnnotationArrayAttributeValue(codebase: PsiBasedCodebase, private val value: UCallExpression) :
316     UAnnotationValue(), AnnotationArrayAttributeValue {
<lambda>null317     override val values = value.valueArguments.map {
318         create(codebase, it)
319     }.toList()
320 
toSourcenull321     override fun toSource(): String = getText(value)
322 }
323 
324 private fun getText(element: UElement): String {
325     return element.sourcePsi?.text ?: element.asSourceString()
326 }