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.DefaultModifierList 21 import com.android.tools.metalava.model.PackageItem 22 import com.android.tools.metalava.model.VisibilityLevel 23 import com.intellij.psi.PsiPackage 24 25 class PsiPackageItem 26 internal constructor( 27 codebase: PsiBasedCodebase, 28 private val psiPackage: PsiPackage, 29 private val qualifiedName: String, 30 modifiers: DefaultModifierList, 31 documentation: String, 32 override val overviewDocumentation: String?, 33 /** True if this package is from the classpath (dependencies). Exposed in [isFromClassPath]. */ 34 private val fromClassPath: Boolean 35 ) : 36 PsiItem( 37 codebase = codebase, 38 modifiers = modifiers, 39 documentation = documentation, 40 element = psiPackage 41 ), 42 PackageItem { 43 44 // Note - top level classes only 45 private val classes: MutableList<ClassItem> = mutableListOf() 46 topLevelClassesnull47 override fun topLevelClasses(): Sequence<ClassItem> = 48 classes.toList().asSequence().filter { it.isTopLevelClass() } 49 50 lateinit var containingPackageField: PsiPackageItem 51 containingClassnull52 override fun containingClass(): ClassItem? = null 53 54 override fun psi() = psiPackage 55 56 override fun containingPackage(): PackageItem? { 57 return if (qualifiedName.isEmpty()) null 58 else { 59 if (!::containingPackageField.isInitialized) { 60 var parentPackage = qualifiedName 61 while (true) { 62 val index = parentPackage.lastIndexOf('.') 63 if (index == -1) { 64 containingPackageField = codebase.findPackage("")!! 65 return containingPackageField 66 } 67 parentPackage = parentPackage.substring(0, index) 68 val pkg = codebase.findPackage(parentPackage) 69 if (pkg != null) { 70 containingPackageField = pkg 71 return pkg 72 } 73 } 74 75 @Suppress("UNREACHABLE_CODE") null 76 } else { 77 containingPackageField 78 } 79 } 80 } 81 addClassnull82 fun addClass(cls: PsiClassItem) { 83 if (!cls.isTopLevelClass()) { 84 // TODO: Stash in a list somewhere to make allClasses() faster? 85 return 86 } 87 88 /* 89 // Temp debugging: 90 val q = cls.qualifiedName() 91 for (c in classes) { 92 if (q == c.qualifiedName()) { 93 assert(false, { "Unexpectedly found class $q already listed in $this" }) 94 return 95 } 96 } 97 */ 98 99 classes.add(cls) 100 cls.containingPackage = this 101 } 102 addClassesnull103 fun addClasses(classList: List<PsiClassItem>) { 104 for (cls in classList) { 105 addClass(cls) 106 } 107 } 108 qualifiedNamenull109 override fun qualifiedName(): String = qualifiedName 110 111 override fun equals(other: Any?): Boolean { 112 if (this === other) { 113 return true 114 } 115 return other is PackageItem && qualifiedName == other.qualifiedName() 116 } 117 hashCodenull118 override fun hashCode(): Int = qualifiedName.hashCode() 119 120 override fun isFromClassPath(): Boolean = fromClassPath 121 122 companion object { 123 fun create( 124 codebase: PsiBasedCodebase, 125 psiPackage: PsiPackage, 126 extraDocs: String?, 127 overviewHtml: String?, 128 fromClassPath: Boolean, 129 ): PsiPackageItem { 130 val commentText = 131 javadoc(psiPackage, codebase.allowReadingComments) + 132 if (extraDocs != null) "\n$extraDocs" else "" 133 val modifiers = modifiers(codebase, psiPackage, commentText) 134 if (modifiers.isPackagePrivate()) { 135 // packages are always public (if not hidden explicitly with private) 136 modifiers.setVisibilityLevel(VisibilityLevel.PUBLIC) 137 } 138 val qualifiedName = psiPackage.qualifiedName 139 140 val pkg = 141 PsiPackageItem( 142 codebase = codebase, 143 psiPackage = psiPackage, 144 qualifiedName = qualifiedName, 145 documentation = commentText, 146 overviewDocumentation = overviewHtml, 147 modifiers = modifiers, 148 fromClassPath = fromClassPath 149 ) 150 return pkg 151 } 152 } 153 } 154