1 /* 2 * Copyright (C) 2023 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.ClassItem 20 import com.android.tools.metalava.model.FilterPredicate 21 import com.android.tools.metalava.model.Import 22 import com.android.tools.metalava.model.SourceFile 23 import com.android.tools.metalava.model.item.DefaultCodebase 24 import com.google.turbine.diag.LineMap 25 import com.google.turbine.tree.Tree.CompUnit 26 import java.util.TreeSet 27 28 internal class TurbineSourceFile( 29 val codebase: DefaultCodebase, 30 val compUnit: CompUnit, 31 ) : SourceFile { 32 getHeaderCommentsnull33 override fun getHeaderComments() = getHeaderComments(compUnit.source().source()) 34 35 override fun classes(): Sequence<ClassItem> { 36 val pkgName = getPackageName(compUnit) 37 val classDecls = compUnit.decls() // Top level class declarations 38 val classNames = classDecls.map { pkgName + "." + it.name().value() } 39 return classNames.asSequence().mapNotNull { codebase.findClass(it) } 40 } 41 equalsnull42 override fun equals(other: Any?): Boolean { 43 if (this === other) return true 44 return other is TurbineSourceFile && compUnit == other.compUnit 45 } 46 hashCodenull47 override fun hashCode(): Int { 48 return compUnit.hashCode() 49 } 50 getImportsnull51 override fun getImports(predicate: FilterPredicate): Collection<Import> { 52 val imports = TreeSet<Import>(compareBy { it.pattern }) 53 54 for (import in compUnit.imports()) { 55 val resolvedName = import.type().dotSeparatedName 56 // Package import 57 if (import.wild()) { 58 val pkgItem = codebase.findPackage(resolvedName) ?: continue 59 if ( 60 predicate.test(pkgItem) && 61 // Also make sure it isn't an empty package (after applying the 62 // filter) 63 // since in that case we'd have an invalid import 64 pkgItem.topLevelClasses().any { it.emit && predicate.test(it) } 65 ) { 66 imports.add(Import(pkgItem)) 67 } 68 } 69 // Not static member import i.e. class import 70 else if (!import.stat()) { 71 val classItem = codebase.resolveClass(resolvedName) ?: continue 72 if (predicate.test(classItem)) { 73 imports.add(Import(classItem)) 74 } 75 } 76 } 77 78 // Next only keep those that are present in any docs; those are the only ones 79 // we need to import 80 if (imports.isNotEmpty()) { 81 return filterImports(imports, predicate) 82 } 83 84 return emptyList() 85 } 86 87 /** 88 * The [LineMap] used to map positions in the source file into line numbers. 89 * 90 * Created lazily as it can be expensive to create. 91 */ 92 private val lineMap by <lambda>null93 lazy(LazyThreadSafetyMode.NONE) { LineMap.create(compUnit.source().source()) } 94 95 /** 96 * Get the line number for [position] which was retrieved from 97 * [com.google.turbine.tree.Tree.position]. 98 */ lineForPositionnull99 fun lineForPosition(position: Int) = lineMap.lineNumber(position) 100 } 101