1 /* <lambda>null2 * Copyright (C) 2017 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.ATTR_VALUE 20 import com.android.tools.lint.detector.api.ConstantEvaluator 21 import com.android.tools.metalava.XmlBackedAnnotationItem 22 import com.android.tools.metalava.model.AnnotationArrayAttributeValue 23 import com.android.tools.metalava.model.AnnotationAttribute 24 import com.android.tools.metalava.model.AnnotationAttributeValue 25 import com.android.tools.metalava.model.AnnotationItem 26 import com.android.tools.metalava.model.AnnotationSingleAttributeValue 27 import com.android.tools.metalava.model.AnnotationTarget 28 import com.android.tools.metalava.model.ClassItem 29 import com.android.tools.metalava.model.Codebase 30 import com.android.tools.metalava.model.DefaultAnnotationItem 31 import com.android.tools.metalava.model.Item 32 import com.android.tools.metalava.model.psi.CodePrinter.Companion.constantToExpression 33 import com.android.tools.metalava.model.psi.CodePrinter.Companion.constantToSource 34 import com.intellij.psi.PsiAnnotation 35 import com.intellij.psi.PsiAnnotationMethod 36 import com.intellij.psi.PsiAnnotationMemberValue 37 import com.intellij.psi.PsiArrayInitializerMemberValue 38 import com.intellij.psi.PsiBinaryExpression 39 import com.intellij.psi.PsiClass 40 import com.intellij.psi.PsiExpression 41 import com.intellij.psi.PsiField 42 import com.intellij.psi.PsiLiteral 43 import com.intellij.psi.PsiMethod 44 import com.intellij.psi.PsiReference 45 import com.intellij.psi.impl.JavaConstantExpressionEvaluator 46 import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation 47 48 class PsiAnnotationItem private constructor( 49 override val codebase: PsiBasedCodebase, 50 val psiAnnotation: PsiAnnotation, 51 private val originalName: String? 52 ) : DefaultAnnotationItem(codebase) { 53 private val qualifiedName = AnnotationItem.mapName(codebase, originalName) 54 55 private var attributes: List<AnnotationAttribute>? = null 56 57 override fun originalName(): String? = originalName 58 59 override fun toString(): String = toSource() 60 61 override fun toSource(target: AnnotationTarget, showDefaultAttrs: Boolean): String { 62 val sb = StringBuilder(60) 63 appendAnnotation(codebase, sb, psiAnnotation, originalName, target, showDefaultAttrs) 64 return sb.toString() 65 } 66 67 override fun resolve(): ClassItem? { 68 return codebase.findOrCreateClass(originalName ?: return null) 69 } 70 71 override fun isNonNull(): Boolean { 72 if (psiAnnotation is KtLightNullabilityAnnotation<*> && 73 originalName == "" 74 ) { 75 // Hack/workaround: some UAST annotation nodes do not provide qualified name :=( 76 return true 77 } 78 return super.isNonNull() 79 } 80 81 override fun qualifiedName() = qualifiedName 82 83 override fun attributes(): List<AnnotationAttribute> { 84 if (attributes == null) { 85 val psiAttributes = psiAnnotation.parameterList.attributes 86 attributes = if (psiAttributes.isEmpty()) { 87 emptyList() 88 } else { 89 val list = mutableListOf<AnnotationAttribute>() 90 for (parameter in psiAttributes) { 91 list.add( 92 PsiAnnotationAttribute( 93 codebase, 94 parameter.name ?: ATTR_VALUE, parameter.value ?: continue 95 ) 96 ) 97 } 98 list 99 } 100 } 101 102 return attributes!! 103 } 104 105 override fun targets(): Set<AnnotationTarget> { 106 if (targets == null) { 107 targets = AnnotationItem.computeTargets(this) { className -> 108 codebase.findOrCreateClass(className) 109 } 110 } 111 return targets!! 112 } 113 114 companion object { 115 fun create(codebase: PsiBasedCodebase, psiAnnotation: PsiAnnotation, qualifiedName: String? = psiAnnotation.qualifiedName): PsiAnnotationItem { 116 return PsiAnnotationItem(codebase, psiAnnotation, qualifiedName) 117 } 118 119 fun create(codebase: PsiBasedCodebase, original: PsiAnnotationItem): PsiAnnotationItem { 120 return PsiAnnotationItem(codebase, original.psiAnnotation, original.originalName) 121 } 122 123 // TODO: Inline this such that instead of constructing XmlBackedAnnotationItem 124 // and then producing source and parsing it, produce source directly 125 fun create( 126 codebase: Codebase, 127 xmlAnnotation: XmlBackedAnnotationItem, 128 context: Item? = null 129 ): PsiAnnotationItem { 130 if (codebase is PsiBasedCodebase) { 131 return codebase.createAnnotation(xmlAnnotation.toSource(), context) 132 } else { 133 codebase.unsupported("Converting to PSI annotation requires PSI codebase") 134 } 135 } 136 137 private fun getAttributes(annotation: PsiAnnotation, showDefaultAttrs: Boolean): 138 List<Pair<String?, PsiAnnotationMemberValue?>> { 139 val annotationClass = annotation.nameReferenceElement?.resolve() as? PsiClass 140 val list = mutableListOf<Pair<String?, PsiAnnotationMemberValue?>>() 141 if (annotationClass != null && showDefaultAttrs) { 142 for (method in annotationClass.methods) { 143 if (method !is PsiAnnotationMethod) { 144 continue 145 } 146 list.add(Pair(method.name, annotation.findAttributeValue(method.name))) 147 } 148 } else { 149 for (attr in annotation.parameterList.attributes) { 150 list.add(Pair(attr.name, attr.value)) 151 } 152 } 153 return list 154 } 155 156 private fun appendAnnotation( 157 codebase: PsiBasedCodebase, 158 sb: StringBuilder, 159 psiAnnotation: PsiAnnotation, 160 originalName: String?, 161 target: AnnotationTarget, 162 showDefaultAttrs: Boolean 163 ) { 164 val qualifiedName = AnnotationItem.mapName(codebase, originalName, null, target) ?: return 165 166 val attributes = getAttributes(psiAnnotation, showDefaultAttrs) 167 if (attributes.isEmpty()) { 168 sb.append("@$qualifiedName") 169 return 170 } 171 172 sb.append("@") 173 sb.append(qualifiedName) 174 sb.append("(") 175 if (attributes.size == 1 && (attributes[0].first == null || attributes[0].first == ATTR_VALUE)) { 176 // Special case: omit "value" if it's the only attribute 177 appendValue(codebase, sb, attributes[0].second, target, showDefaultAttrs) 178 } else { 179 var first = true 180 for (attribute in attributes) { 181 if (first) { 182 first = false 183 } else { 184 sb.append(", ") 185 } 186 sb.append(attribute.first ?: ATTR_VALUE) 187 sb.append('=') 188 appendValue(codebase, sb, attribute.second, target, showDefaultAttrs) 189 } 190 } 191 sb.append(")") 192 } 193 194 private fun appendValue( 195 codebase: PsiBasedCodebase, 196 sb: StringBuilder, 197 value: PsiAnnotationMemberValue?, 198 target: AnnotationTarget, 199 showDefaultAttrs: Boolean 200 ) { 201 // Compute annotation string -- we don't just use value.text here 202 // because that may not use fully qualified names, e.g. the source may say 203 // @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION) 204 // and we want to compute 205 // @android.support.annotation.RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) 206 when (value) { 207 null -> sb.append("null") 208 is PsiLiteral -> sb.append(constantToSource(value.value)) 209 is PsiReference -> { 210 when (val resolved = value.resolve()) { 211 is PsiField -> { 212 val containing = resolved.containingClass 213 if (containing != null) { 214 // If it's a field reference, see if it looks like the field is hidden; if 215 // so, inline the value 216 val cls = codebase.findOrCreateClass(containing) 217 val initializer = resolved.initializer 218 if (initializer != null) { 219 val fieldItem = cls.findField(resolved.name) 220 if (fieldItem == null || fieldItem.isHiddenOrRemoved() || 221 !fieldItem.isPublic) { 222 // Use the literal value instead 223 val source = getConstantSource(initializer) 224 if (source != null) { 225 sb.append(source) 226 return 227 } 228 } 229 } 230 containing.qualifiedName?.let { 231 sb.append(it).append('.') 232 } 233 } 234 235 sb.append(resolved.name) 236 } 237 is PsiClass -> resolved.qualifiedName?.let { sb.append(it) } 238 else -> { 239 sb.append(value.text) 240 } 241 } 242 } 243 is PsiBinaryExpression -> { 244 appendValue(codebase, sb, value.lOperand, target, showDefaultAttrs) 245 sb.append(' ') 246 sb.append(value.operationSign.text) 247 sb.append(' ') 248 appendValue(codebase, sb, value.rOperand, target, showDefaultAttrs) 249 } 250 is PsiArrayInitializerMemberValue -> { 251 sb.append('{') 252 var first = true 253 for (initializer in value.initializers) { 254 if (first) { 255 first = false 256 } else { 257 sb.append(", ") 258 } 259 appendValue(codebase, sb, initializer, target, showDefaultAttrs) 260 } 261 sb.append('}') 262 } 263 is PsiAnnotation -> { 264 appendAnnotation(codebase, sb, value, value.qualifiedName, target, showDefaultAttrs) 265 } 266 else -> { 267 if (value is PsiExpression) { 268 val source = getConstantSource(value) 269 if (source != null) { 270 sb.append(source) 271 return 272 } 273 } 274 sb.append(value.text) 275 } 276 } 277 } 278 279 private fun getConstantSource(value: PsiExpression): String? { 280 val constant = JavaConstantExpressionEvaluator.computeConstantExpression(value, false) 281 return constantToExpression(constant) 282 } 283 } 284 } 285 286 class PsiAnnotationAttribute( 287 codebase: PsiBasedCodebase, 288 override val name: String, 289 psiValue: PsiAnnotationMemberValue 290 ) : AnnotationAttribute { 291 override val value: AnnotationAttributeValue = PsiAnnotationValue.create( 292 codebase, psiValue 293 ) 294 } 295 296 abstract class PsiAnnotationValue : AnnotationAttributeValue { 297 companion object { createnull298 fun create(codebase: PsiBasedCodebase, value: PsiAnnotationMemberValue): PsiAnnotationValue { 299 return if (value is PsiArrayInitializerMemberValue) { 300 PsiAnnotationArrayAttributeValue(codebase, value) 301 } else { 302 PsiAnnotationSingleAttributeValue(codebase, value) 303 } 304 } 305 } 306 toStringnull307 override fun toString(): String = toSource() 308 } 309 310 class PsiAnnotationSingleAttributeValue( 311 private val codebase: PsiBasedCodebase, 312 private val psiValue: PsiAnnotationMemberValue 313 ) : PsiAnnotationValue(), AnnotationSingleAttributeValue { 314 override val valueSource: String = psiValue.text 315 override val value: Any? 316 get() { 317 if (psiValue is PsiLiteral) { 318 return psiValue.value ?: psiValue.text.removeSurrounding("\"") 319 } 320 321 val value = ConstantEvaluator.evaluate(null, psiValue) 322 if (value != null) { 323 return value 324 } 325 326 return psiValue.text ?: psiValue.text.removeSurrounding("\"") 327 } 328 329 override fun value(): Any? = value 330 331 override fun toSource(): String = psiValue.text 332 333 override fun resolve(): Item? { 334 if (psiValue is PsiReference) { 335 when (val resolved = psiValue.resolve()) { 336 is PsiField -> return codebase.findField(resolved) 337 is PsiClass -> return codebase.findOrCreateClass(resolved) 338 is PsiMethod -> return codebase.findMethod(resolved) 339 } 340 } 341 return null 342 } 343 } 344 345 class PsiAnnotationArrayAttributeValue(codebase: PsiBasedCodebase, private val value: PsiArrayInitializerMemberValue) : 346 PsiAnnotationValue(), AnnotationArrayAttributeValue { <lambda>null347 override val values = value.initializers.map { 348 create(codebase, it) 349 }.toList() 350 toSourcenull351 override fun toSource(): String = value.text 352 } 353