• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.model.text.ApiFile
20 import com.android.tools.metalava.model.text.ApiParseException
21 import com.android.tools.metalava.model.text.TextCodebase
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.Codebase
24 import com.android.tools.metalava.model.visitors.ApiVisitor
25 import java.io.File
26 
27 /** Registry of signature files and the corresponding artifact descriptions */
28 class ArtifactTagger {
29     /** Ordered map from signature file to artifact description */
30     private val artifacts = LinkedHashMap<File, String>()
31 
32     /** Registers the given [artifactId] for the APIs found in the given [signatureFile] */
registernull33     fun register(artifactId: String, signatureFile: File) {
34         artifacts[signatureFile] = artifactId
35     }
36 
37     /** Any registered artifacts? */
anynull38     fun any() = artifacts.isNotEmpty()
39 
40     /** Remove all registrations */
41     fun clear() = artifacts.clear()
42 
43     /** Returns the artifacts */
44     private fun getRegistrations(): Collection<Map.Entry<File, String>> = artifacts.entries
45 
46     /**
47      * Applies the artifact registrations in this map to the given [codebase].
48      * If [warnAboutMissing] is true, it will complain if any classes in the API
49      * are found that have not been tagged (e.g. where no artifact signature file
50      * referenced the API.
51      */
52     fun tag(codebase: Codebase, warnAboutMissing: Boolean = true) {
53         if (!any()) {
54             return
55         }
56 
57         // Read through the XML files in order, applying their artifact information
58         // to the Javadoc models.
59         for (artifactSpec in getRegistrations()) {
60             val xmlFile = artifactSpec.key
61             val artifactName = artifactSpec.value
62 
63             val specApi: TextCodebase
64             try {
65                 specApi = ApiFile.parseApi(xmlFile, options.inputKotlinStyleNulls)
66             } catch (e: ApiParseException) {
67                 reporter.report(
68                     Issues.BROKEN_ARTIFACT_FILE, xmlFile,
69                     "Failed to parse $xmlFile for $artifactName artifact data.\n"
70                 )
71                 continue
72             }
73 
74             applyArtifactsFromSpec(artifactName, specApi, codebase)
75         }
76 
77         if (warnAboutMissing) {
78             codebase.accept(object : ApiVisitor() {
79                 override fun visitClass(cls: ClassItem) {
80                     if (cls.artifact == null && cls.isTopLevelClass()) {
81                         reporter.report(
82                             Issues.NO_ARTIFACT_DATA, cls,
83                             "No registered artifact signature file referenced class ${cls.qualifiedName()}"
84                         )
85                     }
86                 }
87             })
88         }
89     }
90 
applyArtifactsFromSpecnull91     private fun applyArtifactsFromSpec(
92         mavenSpec: String,
93         specApi: TextCodebase,
94         codebase: Codebase
95     ) {
96         for (specPkg in specApi.getPackages().packages) {
97             if (!specPkg.emit) {
98                 continue
99             }
100             val pkg = codebase.findPackage(specPkg.qualifiedName()) ?: continue
101             for (cls in pkg.allClasses()) {
102                 if (!cls.emit) {
103                     continue
104                 }
105                 if (cls.artifact == null) {
106                     cls.artifact = mavenSpec
107                 } else {
108                     reporter.report(
109                         Issues.BROKEN_ARTIFACT_FILE, cls,
110                         "Class ${cls.qualifiedName()} belongs to multiple artifacts: ${cls.artifact} and $mavenSpec"
111                     )
112                 }
113             }
114         }
115     }
116 }
117