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