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