1 /* <lambda>null2 * 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.text 18 19 import com.android.tools.metalava.model.ApiVariantSelectors 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.ClassResolver 22 import com.android.tools.metalava.model.ClassTypeItem 23 import com.android.tools.metalava.model.Codebase 24 import com.android.tools.metalava.model.Item 25 import com.android.tools.metalava.model.ItemLanguage 26 import com.android.tools.metalava.model.bestGuessAtFullName 27 import com.android.tools.metalava.model.item.DefaultClassItem 28 import com.android.tools.metalava.model.item.DefaultCodebase 29 import com.android.tools.metalava.model.item.DefaultCodebaseAssembler 30 import com.android.tools.metalava.model.item.DefaultCodebaseFactory 31 import com.android.tools.metalava.model.item.DefaultItemFactory 32 import com.android.tools.metalava.model.item.DefaultPackageItem 33 import com.android.tools.metalava.model.item.PackageDocs 34 import java.io.File 35 36 internal class TextCodebaseAssembler( 37 codebaseFactory: DefaultCodebaseFactory, 38 private val classResolver: ClassResolver?, 39 ) : DefaultCodebaseAssembler() { 40 41 internal val codebase = codebaseFactory(this) 42 43 /** Creates [Item] instances for this. */ 44 override val itemFactory = 45 DefaultItemFactory( 46 codebase = codebase, 47 // Signature files do not contain information about whether an item was originally 48 // created from Java or Kotlin. 49 defaultItemLanguage = ItemLanguage.UNKNOWN, 50 // Signature files have already been separated by API surface variants, so they can use 51 // the same immutable ApiVariantSelectors. 52 defaultVariantSelectorsFactory = ApiVariantSelectors.IMMUTABLE_FACTORY, 53 ) 54 55 fun initialize() { 56 // Make sure that it has a root package. 57 codebase.packageTracker.createInitialPackages(PackageDocs.EMPTY) 58 } 59 60 override fun createClassFromUnderlyingModel(qualifiedName: String) = 61 getOrCreateClass(qualifiedName) 62 63 /** 64 * The [StubKind] required for each class which could not be found, defaults to [StubKind.CLASS] 65 * if not specified. 66 * 67 * Specific types, require a specific type of class, e.g. a type used in an `extends` clause of 68 * a concrete class requires a concrete class, whereas a type used in an `implements` clause of 69 * a concrete class, or an `extends` list of an interface requires an interface. 70 * 71 * Similarly, an annotation must be an annotation type and extends 72 * `java.lang.annotation.Annotation` and a `throws` type that is not a type parameter must be a 73 * concrete class that extends `java.lang.Throwable.` 74 * 75 * This contains information about the type use so that if a stub class is needed a class of the 76 * appropriate structure can be fabricated to avoid spurious issues being reported. 77 */ 78 private val requiredStubKindForClass = mutableMapOf<String, StubKind>() 79 80 override fun newClassRegistered(classItem: DefaultClassItem) { 81 // A real class exists so a stub will not be created so the hint as to the kind of class 82 // that the stubs should be is no longer needed. 83 requiredStubKindForClass.remove(classItem.qualifiedName()) 84 } 85 86 /** 87 * Register that the class type requires a specific stub kind. 88 * 89 * If a concrete class already exists then this does nothing. Otherwise, this registers the 90 * [StubKind] for the [ClassTypeItem.qualifiedName], making sure that it does not conflict with 91 * any previous requirements. 92 */ 93 fun requireStubKindFor(classTypeItem: ClassTypeItem, stubKind: StubKind) { 94 val qualifiedName = classTypeItem.qualifiedName 95 96 // If a real class already exists then a stub will not need to be created. 97 if (codebase.findClass(qualifiedName) != null) return 98 99 val existing = requiredStubKindForClass.put(qualifiedName, stubKind) 100 if (existing != null && existing != stubKind) { 101 error( 102 "Mismatching required stub kinds for $qualifiedName, found $existing and $stubKind" 103 ) 104 } 105 } 106 107 /** 108 * Gets an existing, or creates a new [ClassItem]. 109 * 110 * Tries to find [qualifiedName] in [codebase]. If not found, then if a [classResolver] is 111 * provided it will invoke that and return the [ClassItem] it returns if any. Otherwise, it will 112 * create an empty stub class of the [StubKind] specified in [requiredStubKindForClass] or 113 * [StubKind.CLASS] if no specific [StubKind] was required. 114 * 115 * Initializes outer classes and packages for the created class as needed. 116 * 117 * @param qualifiedName the fully qualified name of the class. 118 * @param isOuterClassOfClassInThisCodebase if `true` then this is searching for an outer class 119 * of a class in this codebase, in which case this must only search classes in this codebase, 120 * otherwise it can search for external classes too. 121 */ 122 internal fun getOrCreateClass( 123 qualifiedName: String, 124 isOuterClassOfClassInThisCodebase: Boolean = false, 125 ): ClassItem { 126 // Check this codebase first, if found then return it. 127 codebase.findClass(qualifiedName)?.let { 128 return it 129 } 130 131 // Only check for external classes if this is not searching for an outer class of a class in 132 // this codebase and there is a class resolver that will populate the external classes. 133 if (!isOuterClassOfClassInThisCodebase && classResolver != null) { 134 // Try and resolve the class, returning it if it was found. 135 classResolver.resolveClass(qualifiedName)?.let { 136 return it 137 } 138 } 139 140 val fullName = bestGuessAtFullName(qualifiedName) 141 142 val outerClass = 143 if (fullName.contains('.')) { 144 // We created a new nested class stub. We need to fully initialize it with outer 145 // classes, themselves possibly stubs 146 val outerName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')) 147 val outerClass = getOrCreateClass(outerName, isOuterClassOfClassInThisCodebase) 148 149 // As outerClass and stubClass are from the same codebase the outerClass must be a 150 // DefaultClassItem so cast it to one so that the code below can use 151 // DefaultClassItem methods. 152 outerClass as DefaultClassItem 153 } else { 154 null 155 } 156 157 // Find/create package 158 val pkg = 159 if (outerClass == null) { 160 val endIndex = qualifiedName.lastIndexOf('.') 161 val pkgPath = if (endIndex != -1) qualifiedName.substring(0, endIndex) else "" 162 codebase.findOrCreatePackage(pkgPath) 163 } else { 164 outerClass.containingPackage() as DefaultPackageItem 165 } 166 167 // Build a stub class of the required kind. 168 val requiredStubKind = requiredStubKindForClass.remove(qualifiedName) ?: StubKind.CLASS 169 170 return StubClassBuilder.build( 171 assembler = this, 172 qualifiedName = qualifiedName, 173 containingClass = outerClass, 174 containingPackage = pkg, 175 ) { 176 // Apply stub kind specific mutations to the stub class being built. 177 requiredStubKind.mutator(this) 178 } 179 } 180 181 companion object { 182 /** Create a [TextCodebaseAssembler]. */ 183 fun createAssembler( 184 location: File, 185 description: String, 186 codebaseConfig: Codebase.Config, 187 classResolver: ClassResolver?, 188 ): TextCodebaseAssembler { 189 val assembler = 190 TextCodebaseAssembler( 191 codebaseFactory = { assembler -> 192 DefaultCodebase( 193 location = location, 194 description = description, 195 preFiltered = true, 196 config = codebaseConfig, 197 trustedApi = true, 198 supportsDocumentation = false, 199 assembler = assembler, 200 ) 201 }, 202 classResolver = classResolver, 203 ) 204 assembler.initialize() 205 206 return assembler 207 } 208 } 209 } 210