1 /* 2 * Copyright (C) 2024 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.item 18 19 import com.android.tools.metalava.model.AnnotationItem 20 import com.android.tools.metalava.model.AnnotationManager 21 import com.android.tools.metalava.model.ClassItem 22 import com.android.tools.metalava.model.Codebase 23 import com.android.tools.metalava.model.DefaultAnnotationItem 24 import com.android.tools.metalava.model.Item 25 import com.android.tools.metalava.model.TypeAliasItem 26 import com.android.tools.metalava.model.api.surface.ApiSurfaces 27 import com.android.tools.metalava.reporter.Issues 28 import com.android.tools.metalava.reporter.Reporter 29 import java.io.File 30 import java.util.HashMap 31 32 private const val CLASS_ESTIMATE = 15000 33 34 /** Base class of [Codebase]s. */ 35 open class DefaultCodebase( 36 final override var location: File, 37 description: String, 38 override val preFiltered: Boolean, 39 final override val config: Codebase.Config, 40 private val trustedApi: Boolean, 41 private val supportsDocumentation: Boolean, 42 val assembler: CodebaseAssembler, 43 ) : Codebase { 44 45 final override val annotationManager: AnnotationManager = config.annotationManager 46 47 final override val apiSurfaces: ApiSurfaces = config.apiSurfaces 48 49 final override var description: String = description 50 private set 51 trustedApinull52 final override fun trustedApi() = trustedApi 53 54 final override fun supportsDocumentation() = supportsDocumentation 55 56 final override fun toString() = description 57 58 override fun dispose() { 59 description += " [disposed]" 60 } 61 62 final override var containsRevertedItem: Boolean = false 63 private set 64 markContainsRevertedItemnull65 override fun markContainsRevertedItem() { 66 containsRevertedItem = true 67 } 68 69 override val reporter: Reporter = config.reporter 70 71 /** Tracks [DefaultPackageItem] use in this [Codebase]. */ 72 val packageTracker = PackageTracker(assembler::createPackageItem) 73 getPackagesnull74 final override fun getPackages() = packageTracker.getPackages() 75 76 final override fun size() = packageTracker.size 77 78 final override fun findPackage(pkgName: String) = packageTracker.findPackage(pkgName) 79 80 fun findOrCreatePackage( 81 packageName: String, 82 packageDocs: PackageDocs = PackageDocs.EMPTY, 83 ) = packageTracker.findOrCreatePackage(packageName, packageDocs) 84 85 /** 86 * Map from fully qualified name to [DefaultClassItem] for every class created by this. 87 * 88 * Classes are added via [registerClass] while initialising the codebase. 89 */ 90 private val allClassesByName = HashMap<String, DefaultClassItem>(CLASS_ESTIMATE) 91 92 /** Find a class created by this [Codebase]. */ 93 fun findClassInCodebase(className: String) = allClassesByName[className] 94 95 /** 96 * A list of the top-level classes declared in the codebase's source (rather than on its 97 * classpath). 98 */ 99 private val topLevelClassesFromSource: MutableList<ClassItem> = ArrayList(CLASS_ESTIMATE) 100 101 final override fun getTopLevelClassesFromSource(): List<ClassItem> { 102 return topLevelClassesFromSource 103 } 104 addTopLevelClassFromSourcenull105 fun addTopLevelClassFromSource(classItem: ClassItem) { 106 topLevelClassesFromSource.add(classItem) 107 } 108 freezeClassesnull109 override fun freezeClasses() { 110 for (classItem in topLevelClassesFromSource) { 111 classItem.freeze() 112 } 113 } 114 115 /** Tracks all known type aliases in the codebase by qualified name. */ 116 private val allTypeAliasesByName = HashMap<String, DefaultTypeAliasItem>() 117 findTypeAliasnull118 override fun findTypeAlias(typeAliasName: String): TypeAliasItem? { 119 return allTypeAliasesByName[typeAliasName] 120 } 121 122 /** 123 * Adds the [typeAlias] to the [Codebase], throwing an error if there is already a type alias 124 * with the same qualified name. 125 */ addTypeAliasnull126 internal fun addTypeAlias(typeAlias: DefaultTypeAliasItem) { 127 if (typeAlias.qualifiedName in allTypeAliasesByName) { 128 error("Duplicate typealias ${typeAlias.qualifiedName}") 129 } 130 allTypeAliasesByName[typeAlias.qualifiedName] = typeAlias 131 } 132 133 /** 134 * Look for classes in this [Codebase]. 135 * 136 * A class can be added to this [Codebase] in two ways: 137 * * Created specifically for this [Codebase], i.e. its [ClassItem.codebase] is this. That can 138 * happen during initialization or because [CodebaseAssembler.createClassFromUnderlyingModel] 139 * creates a [ClassItem] in this [Codebase]. 140 * * Created by another [Codebase] and returned by 141 * [CodebaseAssembler.createClassFromUnderlyingModel], i.e. its [ClassItem.codebase] is NOT 142 * this. 143 */ findClassnull144 final override fun findClass(className: String): ClassItem? = 145 findClassInCodebase(className) ?: externalClassesByName[className] 146 147 /** 148 * Register the class by name, return `true` if the class was registered and `false` if it was 149 * not, i.e. because it is a duplicate. 150 */ 151 fun registerClass(classItem: DefaultClassItem): Boolean { 152 // Check for duplicates, ignore the class if it is a duplicate. 153 val qualifiedName = classItem.qualifiedName() 154 val existing = allClassesByName[qualifiedName] 155 if (existing != null) { 156 reporter.report( 157 Issues.DUPLICATE_SOURCE_CLASS, 158 classItem, 159 "Attempted to register $qualifiedName twice; once from ${existing.fileLocation.path} and this one from ${classItem.fileLocation.path}" 160 ) 161 // The class was not registered. 162 return false 163 } 164 165 // Register it by name. 166 allClassesByName[qualifiedName] = classItem 167 168 // Perform any subclass specific processing on the newly registered class. 169 assembler.newClassRegistered(classItem) 170 171 // The class was registered. 172 return true 173 } 174 175 /** Map from name to an external class that was registered using [] */ 176 private val externalClassesByName = mutableMapOf<String, ClassItem>() 177 178 /** 179 * Looks for an existing class in this [Codebase] and if that cannot be found then delegate to 180 * the [assembler] to see if it can create a class from the underlying model. 181 */ resolveClassnull182 final override fun resolveClass(erasedName: String): ClassItem? { 183 findClass(erasedName)?.let { 184 return it 185 } 186 val created = assembler.createClassFromUnderlyingModel(erasedName) ?: return null 187 // If the returned class was not created as part of this Codebase then register it as an 188 // external class so that findClass(...) will find it next time. 189 if (created.codebase !== this) { 190 // Register as an external class. 191 externalClassesByName[erasedName] = created 192 } 193 return created 194 } 195 createAnnotationnull196 override fun createAnnotation( 197 source: String, 198 context: Item?, 199 ): AnnotationItem? { 200 return DefaultAnnotationItem.create(this, source) 201 } 202 } 203