blob: f09db8d5a87dd772dbf2df4c4f00cd3b420957fa [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.metalava.model.psi
import com.android.tools.metalava.model.ClassItem
import com.android.tools.metalava.model.FieldItem
import com.android.tools.metalava.model.PropertyItem
import com.android.tools.metalava.model.TypeItem
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.psi.KtPropertyAccessor
import org.jetbrains.uast.UClass
class PsiPropertyItem private constructor(
override val codebase: PsiBasedCodebase,
private val psiMethod: PsiMethod,
private val containingClass: PsiClassItem,
private val name: String,
modifiers: PsiModifierItem,
documentation: String,
private val fieldType: PsiTypeItem,
override val getter: PsiMethodItem,
override val setter: PsiMethodItem?,
override val constructorParameter: PsiParameterItem?,
override val backingField: PsiFieldItem?
) :
PsiItem(
codebase = codebase,
modifiers = modifiers,
documentation = documentation,
element = psiMethod
),
PropertyItem {
override fun type(): TypeItem = fieldType
override fun name(): String = name
override fun containingClass(): ClassItem = containingClass
override fun isCloned(): Boolean {
val psiClass = run {
val p = containingClass().psi() as? PsiClass ?: return false
if (p is UClass) {
p.sourcePsi as? PsiClass ?: return false
} else {
p
}
}
return psiMethod.containingClass != psiClass
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
return other is FieldItem && name == other.name() && containingClass == other.containingClass()
}
override fun hashCode(): Int {
return name.hashCode()
}
override fun toString(): String = "field ${containingClass.fullName()}.${name()}"
companion object {
/**
* Creates a new property item, given a [name], [type] and relationships to other items.
*
* Kotlin properties consist of up to four other declarations: Their accessor functions,
* primary constructor parameter, and a backing field. These relationships are useful for
* resolving documentation and exposing the model correctly in Kotlin stubs.
*
* Metalava currently requires all properties to have a [getter]. It does not currently
* support private, `const val`, or [JvmField] properties. Mutable `var` properties usually
* have a [setter], but properties with a private default setter may use direct field
* access instead.
*
* Properties declared in the primary constructor of a class have an associated
* [constructorParameter]. This relationship is important for resolving docs which may
* exist on the constructor parameter.
*
* Most properties on classes without a custom getter have a [backingField] to hold their
* value. This is private except for [JvmField] properties.
*/
fun create(
codebase: PsiBasedCodebase,
containingClass: PsiClassItem,
name: String,
type: PsiTypeItem,
getter: PsiMethodItem,
setter: PsiMethodItem? = null,
constructorParameter: PsiParameterItem? = null,
backingField: PsiFieldItem? = null
): PsiPropertyItem {
val psiMethod = getter.psiMethod
val documentation = when (val sourcePsi = getter.sourcePsi) {
is KtPropertyAccessor -> javadoc(sourcePsi.property)
else -> javadoc(sourcePsi ?: psiMethod)
}
val modifiers = modifiers(codebase, psiMethod, documentation)
val property = PsiPropertyItem(
codebase = codebase,
psiMethod = psiMethod,
containingClass = containingClass,
name = name,
documentation = documentation,
modifiers = modifiers,
fieldType = type,
getter = getter,
setter = setter,
constructorParameter = constructorParameter,
backingField = backingField
)
getter.property = property
setter?.property = property
constructorParameter?.property = property
backingField?.property = property
property.modifiers.setOwner(property)
return property
}
}
}