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.findClass(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 // Use the literal value instead 222 val source = getConstantSource(initializer) 223 if (source != null) { 224 sb.append(source) 225 return 226 } 227 } 228 } 229 containing.qualifiedName?.let { 230 sb.append(it).append('.') 231 } 232 } 233 234 sb.append(resolved.name) 235 } 236 is PsiClass -> resolved.qualifiedName?.let { sb.append(it) } 237 else -> { 238 sb.append(value.text) 239 } 240 } 241 } 242 is PsiBinaryExpression -> { 243 appendValue(codebase, sb, value.lOperand, target, showDefaultAttrs) 244 sb.append(' ') 245 sb.append(value.operationSign.text) 246 sb.append(' ') 247 appendValue(codebase, sb, value.rOperand, target, showDefaultAttrs) 248 } 249 is PsiArrayInitializerMemberValue -> { 250 sb.append('{') 251 var first = true 252 for (initializer in value.initializers) { 253 if (first) { 254 first = false 255 } else { 256 sb.append(", ") 257 } 258 appendValue(codebase, sb, initializer, target, showDefaultAttrs) 259 } 260 sb.append('}') 261 } 262 is PsiAnnotation -> { 263 appendAnnotation(codebase, sb, value, value.qualifiedName, target, showDefaultAttrs) 264 } 265 else -> { 266 if (value is PsiExpression) { 267 val source = getConstantSource(value) 268 if (source != null) { 269 sb.append(source) 270 return 271 } 272 } 273 sb.append(value.text) 274 } 275 } 276 } 277 278 private fun getConstantSource(value: PsiExpression): String? { 279 val constant = JavaConstantExpressionEvaluator.computeConstantExpression(value, false) 280 return constantToExpression(constant) 281 } 282 } 283 } 284 285 class PsiAnnotationAttribute( 286 codebase: PsiBasedCodebase, 287 override val name: String, 288 psiValue: PsiAnnotationMemberValue 289 ) : AnnotationAttribute { 290 override val value: AnnotationAttributeValue = PsiAnnotationValue.create( 291 codebase, psiValue 292 ) 293 } 294 295 abstract class PsiAnnotationValue : AnnotationAttributeValue { 296 companion object { createnull297 fun create(codebase: PsiBasedCodebase, value: PsiAnnotationMemberValue): PsiAnnotationValue { 298 return if (value is PsiArrayInitializerMemberValue) { 299 PsiAnnotationArrayAttributeValue(codebase, value) 300 } else { 301 PsiAnnotationSingleAttributeValue(codebase, value) 302 } 303 } 304 } 305 toStringnull306 override fun toString(): String = toSource() 307 } 308 309 class PsiAnnotationSingleAttributeValue( 310 private val codebase: PsiBasedCodebase, 311 private val psiValue: PsiAnnotationMemberValue 312 ) : PsiAnnotationValue(), AnnotationSingleAttributeValue { 313 override val valueSource: String = psiValue.text 314 override val value: Any? 315 get() { 316 if (psiValue is PsiLiteral) { 317 return psiValue.value ?: psiValue.text.removeSurrounding("\"") 318 } 319 320 val value = ConstantEvaluator.evaluate(null, psiValue) 321 if (value != null) { 322 return value 323 } 324 325 return psiValue.text ?: psiValue.text.removeSurrounding("\"") 326 } 327 328 override fun value(): Any? = value 329 330 override fun toSource(): String = psiValue.text 331 332 override fun resolve(): Item? { 333 if (psiValue is PsiReference) { 334 when (val resolved = psiValue.resolve()) { 335 is PsiField -> return codebase.findField(resolved) 336 is PsiClass -> return codebase.findOrCreateClass(resolved) 337 is PsiMethod -> return codebase.findMethod(resolved) 338 } 339 } 340 return null 341 } 342 } 343 344 class PsiAnnotationArrayAttributeValue(codebase: PsiBasedCodebase, private val value: PsiArrayInitializerMemberValue) : 345 PsiAnnotationValue(), AnnotationArrayAttributeValue { <lambda>null346 override val values = value.initializers.map { 347 create(codebase, it) 348 }.toList() 349 toSourcenull350 override fun toSource(): String = value.text 351 } 352