• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     /** True if this package is from the classpath (dependencies). Exposed in [isFromClassPath]. */
31     private val fromClassPath: Boolean
32 ) :
33     PsiItem(
34         codebase = codebase,
35         modifiers = modifiers,
36         documentation = documentation,
37         element = psiPackage
38     ),
39     PackageItem {
40 
41     // Note - top level classes only
42     private val classes: MutableList<ClassItem> = mutableListOf()
43 
<lambda>null44     override fun topLevelClasses(): Sequence<ClassItem> = classes.toList().asSequence().filter { it.isTopLevelClass() }
45 
46     lateinit var containingPackageField: PsiPackageItem
47 
containingClassnull48     override fun containingClass(strict: Boolean): ClassItem? = null
49 
50     override fun containingPackage(strict: Boolean): PackageItem? {
51         if (!strict) {
52             return this
53         }
54         return if (qualifiedName.isEmpty()) null else {
55             if (!::containingPackageField.isInitialized) {
56                 var parentPackage = qualifiedName
57                 while (true) {
58                     val index = parentPackage.lastIndexOf('.')
59                     if (index == -1) {
60                         containingPackageField = codebase.findPackage("")!!
61                         return containingPackageField
62                     }
63                     parentPackage = parentPackage.substring(0, index)
64                     val pkg = codebase.findPackage(parentPackage)
65                     if (pkg != null) {
66                         containingPackageField = pkg
67                         return pkg
68                     }
69                 }
70 
71                 @Suppress("UNREACHABLE_CODE")
72                 null
73             } else {
74                 containingPackageField
75             }
76         }
77     }
78 
addClassnull79     fun addClass(cls: PsiClassItem) {
80         if (!cls.isTopLevelClass()) {
81             // TODO: Stash in a list somewhere to make allClasses() faster?
82             return
83         }
84 
85         /*
86         // Temp debugging:
87         val q = cls.qualifiedName()
88         for (c in classes) {
89             if (q == c.qualifiedName()) {
90                 assert(false, { "Unexpectedly found class $q already listed in $this" })
91                 return
92             }
93         }
94         */
95 
96         classes.add(cls)
97         cls.containingPackage = this
98     }
99 
addClassesnull100     fun addClasses(classList: List<PsiClassItem>) {
101         for (cls in classList) {
102             addClass(cls)
103         }
104     }
105 
qualifiedNamenull106     override fun qualifiedName(): String = qualifiedName
107 
108     override fun equals(other: Any?): Boolean {
109         if (this === other) {
110             return true
111         }
112         return other is PackageItem && qualifiedName == other.qualifiedName()
113     }
114 
hashCodenull115     override fun hashCode(): Int = qualifiedName.hashCode()
116 
117     override fun toString(): String = "package $qualifiedName"
118 
119     override fun finishInitialization() {
120         super.finishInitialization()
121         val initialClasses = ArrayList(classes)
122         var original = initialClasses.size // classes added after this point will have indices >= original
123         for (cls in initialClasses) {
124             if (cls is PsiClassItem) cls.finishInitialization()
125         }
126 
127         // Finish initialization of any additional classes that were registered during
128         // the above initialization (recursively)
129         while (original < classes.size) {
130             val added = ArrayList(classes.subList(original, classes.size))
131             original = classes.size
132             for (cls in added) {
133                 if (cls is PsiClassItem) cls.finishInitialization()
134             }
135         }
136     }
137 
isFromClassPathnull138     override fun isFromClassPath(): Boolean = fromClassPath
139 
140     companion object {
141         fun create(
142             codebase: PsiBasedCodebase,
143             psiPackage: PsiPackage,
144             extraDocs: String?,
145             fromClassPath: Boolean
146         ): PsiPackageItem {
147             val commentText = javadoc(psiPackage) + if (extraDocs != null) "\n$extraDocs" else ""
148             val modifiers = modifiers(codebase, psiPackage, commentText)
149             if (modifiers.isPackagePrivate()) {
150                 // packages are always public (if not hidden explicitly with private)
151                 modifiers.setVisibilityLevel(VisibilityLevel.PUBLIC)
152             }
153             val qualifiedName = psiPackage.qualifiedName
154 
155             val pkg = PsiPackageItem(
156                 codebase = codebase,
157                 psiPackage = psiPackage,
158                 qualifiedName = qualifiedName,
159                 documentation = commentText,
160                 modifiers = modifiers,
161                 fromClassPath = fromClassPath
162             )
163             pkg.modifiers.setOwner(pkg)
164             return pkg
165         }
166 
167         fun create(codebase: PsiBasedCodebase, original: PsiPackageItem): PsiPackageItem {
168             val pkg = PsiPackageItem(
169                 codebase = codebase,
170                 psiPackage = original.psiPackage,
171                 qualifiedName = original.qualifiedName,
172                 documentation = original.documentation,
173                 modifiers = PsiModifierItem.create(codebase, original.modifiers),
174                 fromClassPath = original.isFromClassPath()
175             )
176             pkg.modifiers.setOwner(pkg)
177             return pkg
178         }
179     }
180 }
181