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
18 
19 import com.android.tools.metalava.cli.common.ExecutionEnvironment
20 import com.android.tools.metalava.model.Codebase
21 import com.android.tools.metalava.model.source.EnvironmentManager
22 import com.android.tools.metalava.model.source.SourceModelProvider
23 import com.android.tools.metalava.model.source.SourceParser
24 import com.android.tools.metalava.reporter.Reporter
25 import java.io.Closeable
26 import java.io.File
27 
28 /** Provides support for loading [Codebase]s from jar files. */
29 sealed interface JarCodebaseLoader {
30 
31     /** Load a [Codebase] from a jar file. */
loadFromJarFilenull32     fun loadFromJarFile(
33         apiJar: File,
34         apiAnalyzerConfig: ApiAnalyzer.Config = ApiAnalyzer.Config(),
35     ): Codebase
36 
37     companion object {
38         /** Create an instance fo [JarCodebaseLoader] from an existing [SourceParser]. */
39         fun createForSourceParser(
40             progressTracker: ProgressTracker,
41             reporter: Reporter,
42             sourceParser: SourceParser,
43         ): JarCodebaseLoader {
44             return FromSourceParser(progressTracker, reporter, sourceParser)
45         }
46     }
47 
48     /** A [JarCodebaseLoader] created from an existing [SourceParser]. */
49     private class FromSourceParser(
50         private val progressTracker: ProgressTracker,
51         private val reporter: Reporter,
52         private val sourceParser: SourceParser,
53     ) : JarCodebaseLoader {
loadFromJarFilenull54         override fun loadFromJarFile(
55             apiJar: File,
56             apiAnalyzerConfig: ApiAnalyzer.Config,
57         ): Codebase {
58             progressTracker.progress("Processing jar file: ")
59 
60             val apiPredicateConfig = apiAnalyzerConfig.apiPredicateConfig
61             val apiEmit =
62                 ApiPredicate(
63                     config = apiPredicateConfig.copy(ignoreShown = true),
64                 )
65             val apiReference = apiEmit
66 
67             val codebase = sourceParser.loadFromJar(apiJar)
68             val analyzer = ApiAnalyzer(sourceParser, codebase, reporter, apiAnalyzerConfig)
69             analyzer.mergeExternalInclusionAnnotations()
70             analyzer.computeApi()
71             analyzer.mergeExternalQualifierAnnotations()
72             analyzer.generateInheritedStubs(apiEmit, apiReference)
73             return codebase
74         }
75     }
76 }
77 
78 /**
79  * A [JarCodebaseLoader] that owns its own [EnvironmentManager] and supports releasing its resources
80  * through the [close] method.
81  */
82 class StandaloneJarCodebaseLoader
83 private constructor(
84     /**
85      * The [EnvironmentManager] that created the [SourceParser] that is used to read the jar files.
86      */
87     private val environmentManager: EnvironmentManager,
88 
89     /** The underlying [JarCodebaseLoader] to which this will delegate the [loadFromJarFile]. */
90     private val delegate: JarCodebaseLoader,
91 ) : Closeable, JarCodebaseLoader by delegate {
92 
93     /** Free up any resources held by [environmentManager]. */
closenull94     override fun close() {
95         environmentManager.close()
96     }
97 
98     companion object {
99         /**
100          * Create a [StandaloneJarCodebaseLoader].
101          *
102          * The caller must ensure that the [close] method is called after this has been finished
103          * with to ensure prompt release of resources, e.g. using `...use { jarCodebaseLoader -> }`.
104          */
createnull105         fun create(
106             executionEnvironment: ExecutionEnvironment,
107             progressTracker: ProgressTracker,
108             reporter: Reporter,
109         ): StandaloneJarCodebaseLoader {
110             val sourceModelProvider = SourceModelProvider.getImplementation("psi")
111 
112             val environmentManager =
113                 sourceModelProvider.createEnvironmentManager(
114                     executionEnvironment.disableStderrDumping()
115                 )
116 
117             val annotationManager = DefaultAnnotationManager()
118 
119             val sourceParser =
120                 environmentManager.createSourceParser(
121                     reporter,
122                     annotationManager,
123                 )
124 
125             val jarLoader =
126                 JarCodebaseLoader.createForSourceParser(
127                     progressTracker,
128                     reporter,
129                     sourceParser,
130                 )
131 
132             return StandaloneJarCodebaseLoader(environmentManager, jarLoader)
133         }
134     }
135 }
136