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.ClassItem
20 import com.android.tools.metalava.model.PackageItem
21 import com.android.tools.metalava.model.VisibilityLevel
22 import com.intellij.psi.PsiPackage
23 
24 class PsiPackageItem(
25     override val codebase: PsiBasedCodebase,
26     private val psiPackage: PsiPackage,
27     private val qualifiedName: String,
28     modifiers: PsiModifierItem,
29     documentation: String
30 ) :
31     PsiItem(
32         codebase = codebase,
33         modifiers = modifiers,
34         documentation = documentation,
35         element = psiPackage
36     ), PackageItem {
37     // Note - top level classes only
38     private val classes: MutableList<PsiClassItem> = mutableListOf()
39 
<lambda>null40     override fun topLevelClasses(): Sequence<ClassItem> = classes.toList().asSequence().filter { it.isTopLevelClass() }
41 
42     lateinit var containingPackageField: PsiPackageItem
43 
containingClassnull44     override fun containingClass(strict: Boolean): ClassItem? = null
45 
46     override fun containingPackage(strict: Boolean): PackageItem? {
47         if (!strict) {
48             return this
49         }
50         return if (qualifiedName.isEmpty()) null else {
51             if (!::containingPackageField.isInitialized) {
52                 var parentPackage = qualifiedName
53                 while (true) {
54                     val index = parentPackage.lastIndexOf('.')
55                     if (index == -1) {
56                         containingPackageField = codebase.findPackage("")!!
57                         return containingPackageField
58                     }
59                     parentPackage = parentPackage.substring(0, index)
60                     val pkg = codebase.findPackage(parentPackage)
61                     if (pkg != null) {
62                         containingPackageField = pkg
63                         return pkg
64                     }
65                 }
66 
67                 @Suppress("UNREACHABLE_CODE")
68                 null
69             } else {
70                 containingPackageField
71             }
72         }
73     }
74 
addClassnull75     fun addClass(cls: PsiClassItem) {
76         if (!cls.isTopLevelClass()) {
77             // TODO: Stash in a list somewhere to make allClasses() faster?
78             return
79         }
80 
81         /*
82         // Temp debugging:
83         val q = cls.qualifiedName()
84         for (c in classes) {
85             if (q == c.qualifiedName()) {
86                 assert(false, { "Unexpectedly found class $q already listed in $this" })
87                 return
88             }
89         }
90         */
91 
92         classes.add(cls)
93         cls.containingPackage = this
94     }
95 
addClassesnull96     fun addClasses(classList: List<PsiClassItem>) {
97         for (cls in classList) {
98             addClass(cls)
99         }
100     }
101 
qualifiedNamenull102     override fun qualifiedName(): String = qualifiedName
103 
104     override fun equals(other: Any?): Boolean {
105         if (this === other) {
106             return true
107         }
108         return other is PackageItem && qualifiedName == other.qualifiedName()
109     }
110 
hashCodenull111     override fun hashCode(): Int = qualifiedName.hashCode()
112 
113     override fun toString(): String = "package $qualifiedName"
114 
115     override fun finishInitialization() {
116         super.finishInitialization()
117         val initialClasses = ArrayList(classes)
118         var original = initialClasses.size // classes added after this point will have indices >= original
119         for (cls in initialClasses) {
120             cls.finishInitialization()
121         }
122 
123         // Finish initialization of any additional classes that were registered during
124         // the above initialization (recursively)
125         while (original < classes.size) {
126             val added = ArrayList(classes.subList(original, classes.size))
127             original = classes.size
128             for (cls in added) {
129                 cls.finishInitialization()
130             }
131         }
132     }
133 
134     companion object {
createnull135         fun create(codebase: PsiBasedCodebase, psiPackage: PsiPackage, extraDocs: String?): PsiPackageItem {
136             val commentText = javadoc(psiPackage) + if (extraDocs != null) "\n$extraDocs" else ""
137             val modifiers = modifiers(codebase, psiPackage, commentText)
138             if (modifiers.isPackagePrivate()) {
139                 // packages are always public (if not hidden explicitly with private)
140                 modifiers.setVisibilityLevel(VisibilityLevel.PUBLIC)
141             }
142             val qualifiedName = psiPackage.qualifiedName
143 
144             val pkg = PsiPackageItem(
145                 codebase = codebase,
146                 psiPackage = psiPackage,
147                 qualifiedName = qualifiedName,
148                 documentation = commentText,
149                 modifiers = modifiers
150             )
151             pkg.modifiers.setOwner(pkg)
152             return pkg
153         }
154 
createnull155         fun create(codebase: PsiBasedCodebase, original: PsiPackageItem): PsiPackageItem {
156             val pkg = PsiPackageItem(
157                 codebase = codebase,
158                 psiPackage = original.psiPackage,
159                 qualifiedName = original.qualifiedName,
160                 documentation = original.documentation,
161                 modifiers = PsiModifierItem.create(codebase, original.modifiers)
162             )
163             pkg.modifiers.setOwner(pkg)
164             return pkg
165         }
166     }
167 }
168