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