• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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