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.ClassTypeItem
20 import com.android.tools.metalava.model.ConstructorItem
21 import com.android.tools.metalava.model.DefaultModifierList
22 import com.android.tools.metalava.model.DefaultModifierList.Companion.PACKAGE_PRIVATE
23 import com.android.tools.metalava.model.ExceptionTypeItem
24 import com.android.tools.metalava.model.MethodItem
25 import com.android.tools.metalava.model.TypeParameterList
26 import com.android.tools.metalava.reporter.FileLocation
27 import com.intellij.psi.JavaPsiFacade
28 import com.intellij.psi.PsiClass
29 import com.intellij.psi.PsiMethod
30 import org.jetbrains.kotlin.psi.KtClassOrObject
31 import org.jetbrains.kotlin.psi.KtPrimaryConstructor
32 import org.jetbrains.uast.UMethod
33 
34 class PsiConstructorItem
35 private constructor(
36     codebase: PsiBasedCodebase,
37     psiMethod: PsiMethod,
38     fileLocation: FileLocation = PsiFileLocation(psiMethod),
39     containingClass: PsiClassItem,
40     name: String,
41     modifiers: DefaultModifierList,
42     documentation: String,
43     parameters: List<PsiParameterItem>,
44     returnType: ClassTypeItem,
45     typeParameterList: TypeParameterList,
46     throwsTypes: List<ExceptionTypeItem>,
47     val implicitConstructor: Boolean = false,
48     override val isPrimary: Boolean = false
49 ) :
50     PsiMethodItem(
51         codebase = codebase,
52         modifiers = modifiers,
53         documentation = documentation,
54         psiMethod = psiMethod,
55         fileLocation = fileLocation,
56         containingClass = containingClass,
57         name = name,
58         returnType = returnType,
59         parameters = parameters,
60         typeParameterList = typeParameterList,
61         throwsTypes = throwsTypes,
62     ),
63     ConstructorItem {
64 
isImplicitConstructornull65     override fun isImplicitConstructor(): Boolean = implicitConstructor
66 
67     override fun isConstructor(): Boolean = true
68 
69     override var superConstructor: ConstructorItem? = null
70 
71     override fun superMethods(): List<MethodItem> = emptyList()
72 
73     companion object {
74         internal fun create(
75             codebase: PsiBasedCodebase,
76             containingClass: PsiClassItem,
77             psiMethod: PsiMethod,
78             enclosingClassTypeItemFactory: PsiTypeItemFactory,
79         ): PsiConstructorItem {
80             assert(psiMethod.isConstructor)
81             val name = psiMethod.name
82             val commentText = javadoc(psiMethod, codebase.allowReadingComments)
83             val modifiers = modifiers(codebase, psiMethod, commentText)
84             // Create the TypeParameterList for this before wrapping any of the other types used by
85             // it as they may reference a type parameter in the list.
86             val (typeParameterList, constructorTypeItemFactory) =
87                 PsiTypeParameterList.create(
88                     codebase,
89                     enclosingClassTypeItemFactory,
90                     "constructor $name",
91                     psiMethod
92                 )
93             val parameters = parameterList(codebase, psiMethod, constructorTypeItemFactory)
94             val constructor =
95                 PsiConstructorItem(
96                     codebase = codebase,
97                     psiMethod = psiMethod,
98                     containingClass = containingClass,
99                     name = name,
100                     documentation = commentText,
101                     modifiers = modifiers,
102                     parameters = parameters,
103                     returnType = containingClass.type(),
104                     implicitConstructor = false,
105                     isPrimary = (psiMethod as? UMethod)?.isPrimaryConstructor ?: false,
106                     typeParameterList = typeParameterList,
107                     throwsTypes = throwsTypes(psiMethod, constructorTypeItemFactory),
108                 )
109             return constructor
110         }
111 
112         fun createDefaultConstructor(
113             codebase: PsiBasedCodebase,
114             containingClass: PsiClassItem,
115             psiClass: PsiClass,
116         ): PsiConstructorItem {
117             val name = psiClass.name!!
118 
119             val factory = JavaPsiFacade.getInstance(psiClass.project).elementFactory
120             val psiMethod = factory.createConstructor(name, psiClass)
121             val modifiers = DefaultModifierList(codebase, PACKAGE_PRIVATE, null)
122             modifiers.setVisibilityLevel(containingClass.modifiers.getVisibilityLevel())
123 
124             val item =
125                 PsiConstructorItem(
126                     codebase = codebase,
127                     psiMethod = psiMethod,
128                     // Use the location of the containing class for the implicit default
129                     // constructor.
130                     fileLocation = containingClass.fileLocation,
131                     containingClass = containingClass,
132                     name = name,
133                     documentation = "",
134                     modifiers = modifiers,
135                     parameters = emptyList(),
136                     returnType = containingClass.type(),
137                     implicitConstructor = true,
138                     typeParameterList = TypeParameterList.NONE,
139                     throwsTypes = emptyList(),
140                 )
141             return item
142         }
143 
144         private val UMethod.isPrimaryConstructor: Boolean
145             get() = sourcePsi is KtPrimaryConstructor || sourcePsi is KtClassOrObject
146     }
147 }
148