• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.tools.metalava.model.ConstructorItem
20 import com.android.tools.metalava.model.MethodItem
21 import com.intellij.psi.JavaPsiFacade
22 import com.intellij.psi.PsiClass
23 import com.intellij.psi.PsiElement
24 import com.intellij.psi.PsiExpressionStatement
25 import com.intellij.psi.PsiKeyword
26 import com.intellij.psi.PsiMethod
27 import com.intellij.psi.PsiMethodCallExpression
28 import com.intellij.psi.PsiWhiteSpace
29 
30 class PsiConstructorItem(
31     codebase: PsiBasedCodebase,
32     psiMethod: PsiMethod,
33     containingClass: PsiClassItem,
34     name: String,
35     modifiers: PsiModifierItem,
36     documentation: String,
37     parameters: List<PsiParameterItem>,
38     returnType: PsiTypeItem,
39     val implicitConstructor: Boolean = false
40 ) :
41     PsiMethodItem(
42         codebase = codebase,
43         modifiers = modifiers,
44         documentation = documentation,
45         psiMethod = psiMethod,
46         containingClass = containingClass,
47         name = name,
48         returnType = returnType,
49         parameters = parameters
50     ), ConstructorItem {
51 
52     init {
53         if (implicitConstructor) {
54             setThrowsTypes(emptyList())
55         }
56     }
57 
58     override fun isImplicitConstructor(): Boolean = implicitConstructor
59     override fun isConstructor(): Boolean = true
60     override var superConstructor: ConstructorItem? = null
61     override fun isCloned(): Boolean = false
62 
63     private var _superMethods: List<MethodItem>? = null
64     override fun superMethods(): List<MethodItem> {
65         if (_superMethods == null) {
66             val result = mutableListOf<MethodItem>()
67             psiMethod.findSuperMethods().mapTo(result) { codebase.findMethod(it) }
68 
69             if (result.isEmpty() && isConstructor() && containingClass().superClass() != null) {
70                 // Try a little harder; psi findSuperMethod doesn't seem to find super constructors in
71                 // some cases, but maybe we can find it by resolving actual super() calls!
72                 // TODO: Port to UAST
73                 var curr: PsiElement? = psiMethod.body?.firstBodyElement
74                 while (curr != null && curr is PsiWhiteSpace) {
75                     curr = curr.nextSibling
76                 }
77                 if (curr is PsiExpressionStatement && curr.expression is PsiMethodCallExpression &&
78                     curr.expression.firstChild?.lastChild is PsiKeyword &&
79                     curr.expression.firstChild?.lastChild?.text == "super"
80                 ) {
81                     val resolved = (curr.expression as PsiMethodCallExpression).resolveMethod()
82                     if (resolved is PsiMethod) {
83                         val superConstructor = codebase.findMethod(resolved)
84                         result.add(superConstructor)
85                     }
86                 }
87             }
88             _superMethods = result
89         }
90 
91         return _superMethods!!
92     }
93 
94     override fun psi(): PsiElement? {
95         // If no PSI element, is this a synthetic/implicit constructor? If so
96         // grab the parent class' PSI element instead for file/location purposes
97         if (implicitConstructor && element.containingFile?.virtualFile == null) {
98             return containingClass().psi()
99         }
100 
101         return element
102     }
103 
104     companion object {
105         fun create(
106             codebase: PsiBasedCodebase,
107             containingClass: PsiClassItem,
108             psiMethod: PsiMethod
109         ): PsiConstructorItem {
110             assert(psiMethod.isConstructor)
111             val name = psiMethod.name
112             val commentText = javadoc(psiMethod)
113             val modifiers = modifiers(codebase, psiMethod, commentText)
114             val parameters = psiMethod.parameterList.parameters.mapIndexed { index, parameter ->
115                 PsiParameterItem.create(codebase, parameter, index)
116             }
117 
118             val constructor = PsiConstructorItem(
119                 codebase = codebase,
120                 psiMethod = psiMethod,
121                 containingClass = containingClass,
122                 name = name,
123                 documentation = commentText,
124                 modifiers = modifiers,
125                 parameters = parameters,
126                 returnType = codebase.getType(containingClass.psiClass),
127                 implicitConstructor = false
128             )
129             constructor.modifiers.setOwner(constructor)
130             return constructor
131         }
132 
133         fun createDefaultConstructor(
134             codebase: PsiBasedCodebase,
135             containingClass: PsiClassItem,
136             psiClass: PsiClass
137         ): PsiConstructorItem {
138             val name = psiClass.name!!
139 
140             val factory = JavaPsiFacade.getInstance(psiClass.project).elementFactory
141             val psiMethod = factory.createConstructor(name, psiClass)
142             val flags = containingClass.modifiers.getAccessFlags()
143             val modifiers = PsiModifierItem(codebase, flags, null)
144 
145             val item = PsiConstructorItem(
146                 codebase = codebase,
147                 psiMethod = psiMethod,
148                 containingClass = containingClass,
149                 name = name,
150                 documentation = "",
151                 modifiers = modifiers,
152                 parameters = emptyList(),
153                 returnType = codebase.getType(psiClass),
154                 implicitConstructor = true
155             )
156             modifiers.setOwner(item)
157             return item
158         }
159     }
160 }