1 /* 2 * 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.tools.metalava.model.MethodItem 20 import com.android.tools.metalava.model.ParameterItem 21 import com.android.tools.metalava.model.TypeItem 22 import com.intellij.psi.PsiParameter 23 import org.jetbrains.kotlin.psi.KtNamedFunction 24 import org.jetbrains.kotlin.psi.KtParameter 25 import org.jetbrains.kotlin.psi.psiUtil.parameterIndex 26 import org.jetbrains.uast.kotlin.declarations.KotlinUMethod 27 28 class PsiParameterItem( 29 override val codebase: PsiBasedCodebase, 30 private val psiParameter: PsiParameter, 31 private val name: String, 32 override val parameterIndex: Int, 33 modifiers: PsiModifierItem, 34 documentation: String, 35 private val type: PsiTypeItem 36 ) : PsiItem( 37 codebase = codebase, 38 modifiers = modifiers, 39 documentation = documentation, 40 element = psiParameter 41 ), ParameterItem { 42 lateinit var containingMethod: PsiMethodItem 43 namenull44 override fun name(): String = name 45 46 override fun publicName(): String? { 47 if (isKotlin(psiParameter)) { 48 if (name == "\$receiver") { 49 return null 50 } 51 return name 52 } else { 53 // Java: Look for @ParameterName annotation 54 val annotation = modifiers.annotations().firstOrNull { it.isParameterName() } 55 if (annotation != null) { 56 return annotation.attributes().firstOrNull()?.value?.value()?.toString() 57 } 58 } 59 60 return null 61 } 62 hasDefaultValuenull63 override fun hasDefaultValue(): Boolean { 64 return if (isKotlin(psiParameter)) { 65 getKtParameter()?.hasDefaultValue() ?: false 66 } else { 67 // Java: Look for @ParameterName annotation 68 modifiers.annotations().any { it.isDefaultValue() } 69 } 70 } 71 getKtParameternull72 private fun getKtParameter(): KtParameter? { 73 val ktParameters = 74 ((containingMethod.psiMethod as? KotlinUMethod)?.sourcePsi as? KtNamedFunction)?.valueParameters 75 ?: return null 76 77 // Perform matching based on parameter names, because indices won't work in the 78 // presence of @JvmOverloads where UAST generates multiple permutations of the 79 // method from the same KtParameters array. 80 81 // Quick lookup first which usually works (lined up from the end to account 82 // for receivers for extension methods etc) 83 val rem = containingMethod.parameters().size - parameterIndex 84 val index = ktParameters.size - rem 85 if (index >= 0) { 86 val parameter = ktParameters[index] 87 if (parameter.name == name) { 88 return parameter 89 } 90 } 91 92 for (parameter in ktParameters) { 93 if (parameter.name == name) { 94 return parameter 95 } 96 } 97 98 // Fallback to handle scenario where the real parameter names are hidden by 99 // UAST (see UastKotlinPsiParameter which replaces parameter names to p$index) 100 if (index >= 0) { 101 val parameter = ktParameters[index] 102 if (name != "\$receiver") { 103 return parameter 104 } 105 } 106 107 return null 108 } 109 defaultValuenull110 override fun defaultValue(): String? { 111 if (isKotlin(psiParameter)) { 112 val ktParameter = getKtParameter() ?: return null 113 if (ktParameter.hasDefaultValue()) { 114 return ktParameter.defaultValue?.text 115 } 116 117 return null 118 } else { 119 // Java: Look for @ParameterName annotation 120 val annotation = modifiers.annotations().firstOrNull { it.isDefaultValue() } 121 if (annotation != null) { 122 return annotation.attributes().firstOrNull()?.value?.value()?.toString() 123 } 124 } 125 126 return null 127 } 128 typenull129 override fun type(): TypeItem = type 130 override fun containingMethod(): MethodItem = containingMethod 131 132 override fun equals(other: Any?): Boolean { 133 if (this === other) { 134 return true 135 } 136 return other is ParameterItem && parameterIndex == other.parameterIndex && containingMethod == other.containingMethod() 137 } 138 hashCodenull139 override fun hashCode(): Int { 140 return parameterIndex 141 } 142 toStringnull143 override fun toString(): String = "parameter ${name()}" 144 145 override fun isVarArgs(): Boolean { 146 return psiParameter.isVarArgs || modifiers.isVarArg() 147 } 148 149 companion object { createnull150 fun create( 151 codebase: PsiBasedCodebase, 152 psiParameter: PsiParameter, 153 parameterIndex: Int 154 ): PsiParameterItem { 155 val name = psiParameter.name ?: "arg${psiParameter.parameterIndex() + 1}" 156 val commentText = "" // no javadocs on individual parameters 157 val modifiers = modifiers(codebase, psiParameter, commentText) 158 val type = codebase.getType(psiParameter.type) 159 val parameter = PsiParameterItem( 160 codebase = codebase, 161 psiParameter = psiParameter, 162 name = name, 163 parameterIndex = parameterIndex, 164 documentation = commentText, 165 modifiers = modifiers, 166 type = type 167 ) 168 parameter.modifiers.setOwner(parameter) 169 return parameter 170 } 171 createnull172 fun create( 173 codebase: PsiBasedCodebase, 174 original: PsiParameterItem 175 ): PsiParameterItem { 176 val parameter = PsiParameterItem( 177 codebase = codebase, 178 psiParameter = original.psiParameter, 179 name = original.name, 180 parameterIndex = original.parameterIndex, 181 documentation = original.documentation, 182 modifiers = PsiModifierItem.create(codebase, original.modifiers), 183 type = PsiTypeItem.create(codebase, original.type) 184 ) 185 parameter.modifiers.setOwner(parameter) 186 return parameter 187 } 188 createnull189 fun create( 190 codebase: PsiBasedCodebase, 191 original: List<ParameterItem> 192 ): List<PsiParameterItem> { 193 return original.map { create(codebase, it as PsiParameterItem) } 194 } 195 } 196 }