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.turbine
18 
19 import com.android.tools.metalava.model.AnnotationItem
20 import com.android.tools.metalava.model.AnnotationManager
21 import com.android.tools.metalava.model.CLASS_ESTIMATE
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.DefaultAnnotationItem
24 import com.android.tools.metalava.model.DefaultCodebase
25 import com.android.tools.metalava.model.Item
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.PackageList
28 import com.android.tools.metalava.model.source.SourceCodebase
29 import com.google.turbine.tree.Tree.CompUnit
30 import java.io.File
31 
32 const val PACKAGE_ESTIMATE = 500
33 
34 internal open class TurbineBasedCodebase(
35     location: File,
36     description: String = "Unknown",
37     annotationManager: AnnotationManager,
38     val allowReadingComments: Boolean
39 ) : DefaultCodebase(location, description, false, annotationManager), SourceCodebase {
40 
41     /**
42      * Map from class name to class item. Classes are added via [registerClass] while initialising
43      * the codebase
44      */
45     private lateinit var classMap: MutableMap<String, TurbineClassItem>
46 
47     /** Map from package name to the corresponding package item */
48     private lateinit var packageMap: MutableMap<String, PackageItem>
49     private val hiddenPackages = mutableSetOf<String>()
50 
51     /**
52      * A list of the top-level classes declared in the codebase's source (rather than on its
53      * classpath).
54      */
55     private lateinit var topLevelClassesFromSource: MutableList<ClassItem>
56 
57     private lateinit var initializer: TurbineCodebaseInitialiser
58 
createAnnotationnull59     override fun createAnnotation(
60         source: String,
61         context: Item?,
62     ): AnnotationItem {
63         return DefaultAnnotationItem.create(this, source)
64     }
65 
findClassnull66     override fun findClass(className: String): TurbineClassItem? {
67         return classMap[className]
68     }
69 
resolveClassnull70     override fun resolveClass(className: String) = findOrCreateClass(className)
71 
72     fun findOrCreateClass(className: String): TurbineClassItem? {
73         return initializer.findOrCreateClass(className)
74     }
75 
findPackagenull76     override fun findPackage(pkgName: String): PackageItem? {
77         return packageMap[pkgName]
78     }
79 
getPackagesnull80     override fun getPackages(): PackageList {
81         return PackageList(
82             this,
83             packageMap.values.toMutableList().sortedWith(PackageItem.comparator)
84         )
85     }
86 
sizenull87     override fun size(): Int {
88         return packageMap.size
89     }
90 
supportsDocumentationnull91     override fun supportsDocumentation(): Boolean = true
92 
93     override fun getTopLevelClassesFromSource(): List<ClassItem> {
94         return topLevelClassesFromSource
95     }
96 
registerClassnull97     fun registerClass(classItem: TurbineClassItem, isTopClass: Boolean) {
98         val qualifiedName = classItem.qualifiedName()
99         val existing = classMap.put(qualifiedName, classItem)
100         if (existing != null) {
101             error(
102                 "Attempted to register $qualifiedName twice; once from ${existing.fileLocation.path} and this one from ${classItem.fileLocation.path}"
103             )
104         }
105 
106         if (isTopClass) {
107             topLevelClassesFromSource.add(classItem)
108         }
109 
110         addClass(classItem)
111     }
112 
113     /**
114      * Determines if a given package name is marked as hidden.
115      *
116      * @param packageName the name of the package to check.
117      * @return true if the package is hidden, false otherwise.
118      */
isPackageHiddennull119     fun isPackageHidden(packageName: String): Boolean {
120         return hiddenPackages.contains(packageName)
121     }
122 
addPackagenull123     fun addPackage(packageItem: TurbinePackageItem) {
124         packageMap.put(packageItem.qualifiedName(), packageItem)
125     }
126 
initializenull127     fun initialize(units: List<CompUnit>, classpath: List<File>, hiddenPackages: Set<String>) {
128         this.hiddenPackages.addAll(hiddenPackages)
129         topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE)
130         classMap = HashMap(CLASS_ESTIMATE)
131         packageMap = HashMap(PACKAGE_ESTIMATE)
132         initializer = TurbineCodebaseInitialiser(units, this, classpath)
133         initializer.initialize()
134     }
135 }
136