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.tools.metalava.model.ClassItem 20 import com.android.tools.metalava.model.FieldItem 21 import com.android.tools.metalava.model.PropertyItem 22 import com.android.tools.metalava.model.TypeItem 23 import com.intellij.psi.PsiClass 24 import com.intellij.psi.PsiMethod 25 import org.jetbrains.kotlin.psi.KtPropertyAccessor 26 import org.jetbrains.uast.UClass 27 28 class PsiPropertyItem private constructor( 29 override val codebase: PsiBasedCodebase, 30 private val psiMethod: PsiMethod, 31 private val containingClass: PsiClassItem, 32 private val name: String, 33 modifiers: PsiModifierItem, 34 documentation: String, 35 private val fieldType: PsiTypeItem, 36 override val getter: PsiMethodItem, 37 override val setter: PsiMethodItem?, 38 override val constructorParameter: PsiParameterItem?, 39 override val backingField: PsiFieldItem? 40 ) : 41 PsiItem( 42 codebase = codebase, 43 modifiers = modifiers, 44 documentation = documentation, 45 element = psiMethod 46 ), 47 PropertyItem { 48 typenull49 override fun type(): TypeItem = fieldType 50 override fun name(): String = name 51 override fun containingClass(): ClassItem = containingClass 52 53 override fun isCloned(): Boolean { 54 val psiClass = run { 55 val p = containingClass().psi() as? PsiClass ?: return false 56 if (p is UClass) { 57 p.sourcePsi as? PsiClass ?: return false 58 } else { 59 p 60 } 61 } 62 return psiMethod.containingClass != psiClass 63 } 64 equalsnull65 override fun equals(other: Any?): Boolean { 66 if (this === other) { 67 return true 68 } 69 return other is FieldItem && name == other.name() && containingClass == other.containingClass() 70 } 71 hashCodenull72 override fun hashCode(): Int { 73 return name.hashCode() 74 } 75 toStringnull76 override fun toString(): String = "field ${containingClass.fullName()}.${name()}" 77 78 companion object { 79 /** 80 * Creates a new property item, given a [name], [type] and relationships to other items. 81 * 82 * Kotlin properties consist of up to four other declarations: Their accessor functions, 83 * primary constructor parameter, and a backing field. These relationships are useful for 84 * resolving documentation and exposing the model correctly in Kotlin stubs. 85 * 86 * Metalava currently requires all properties to have a [getter]. It does not currently 87 * support private, `const val`, or [JvmField] properties. Mutable `var` properties usually 88 * have a [setter], but properties with a private default setter may use direct field 89 * access instead. 90 * 91 * Properties declared in the primary constructor of a class have an associated 92 * [constructorParameter]. This relationship is important for resolving docs which may 93 * exist on the constructor parameter. 94 * 95 * Most properties on classes without a custom getter have a [backingField] to hold their 96 * value. This is private except for [JvmField] properties. 97 */ 98 fun create( 99 codebase: PsiBasedCodebase, 100 containingClass: PsiClassItem, 101 name: String, 102 type: PsiTypeItem, 103 getter: PsiMethodItem, 104 setter: PsiMethodItem? = null, 105 constructorParameter: PsiParameterItem? = null, 106 backingField: PsiFieldItem? = null 107 ): PsiPropertyItem { 108 val psiMethod = getter.psiMethod 109 val documentation = when (val sourcePsi = getter.sourcePsi) { 110 is KtPropertyAccessor -> javadoc(sourcePsi.property) 111 else -> javadoc(sourcePsi ?: psiMethod) 112 } 113 val modifiers = modifiers(codebase, psiMethod, documentation) 114 val property = PsiPropertyItem( 115 codebase = codebase, 116 psiMethod = psiMethod, 117 containingClass = containingClass, 118 name = name, 119 documentation = documentation, 120 modifiers = modifiers, 121 fieldType = type, 122 getter = getter, 123 setter = setter, 124 constructorParameter = constructorParameter, 125 backingField = backingField 126 ) 127 getter.property = property 128 setter?.property = property 129 constructorParameter?.property = property 130 backingField?.property = property 131 property.modifiers.setOwner(property) 132 return property 133 } 134 } 135 } 136