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.Import 21 import com.android.tools.metalava.model.Item 22 import com.android.tools.metalava.model.SourceFile 23 import com.google.turbine.diag.LineMap 24 import com.google.turbine.tree.Tree.CompUnit 25 import java.util.TreeSet 26 import java.util.function.Predicate 27 28 internal class TurbineSourceFile( 29 val codebase: TurbineBasedCodebase, 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: Predicate<Item>): Collection<Import> { 52 val imports = TreeSet<Import>(compareBy { it.pattern }) 53 54 for (import in compUnit.imports()) { 55 val resolvedName = extractNameFromIdent(import.type()) 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.findClass(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