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