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.Issues 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 specApi = ApiFile.parseApi(xmlFile, options.inputKotlinStyleNulls) 67 } catch (e: ApiParseException) { 68 reporter.report( 69 Issues.BROKEN_ARTIFACT_FILE, xmlFile, 70 "Failed to parse $xmlFile for $artifactName artifact data.\n" 71 ) 72 continue 73 } 74 75 applyArtifactsFromSpec(artifactName, specApi, codebase) 76 } 77 78 if (warnAboutMissing) { 79 codebase.accept(object : ApiVisitor() { 80 override fun visitClass(cls: ClassItem) { 81 if (cls.artifact == null && cls.isTopLevelClass()) { 82 reporter.report( 83 Issues.NO_ARTIFACT_DATA, cls, 84 "No registered artifact signature file referenced class ${cls.qualifiedName()}" 85 ) 86 } 87 } 88 }) 89 } 90 } 91 applyArtifactsFromSpecnull92 private fun applyArtifactsFromSpec( 93 mavenSpec: String, 94 specApi: TextCodebase, 95 codebase: Codebase 96 ) { 97 for (specPkg in specApi.getPackages().packages) { 98 if (!specPkg.emit) { 99 continue 100 } 101 val pkg = codebase.findPackage(specPkg.qualifiedName()) ?: continue 102 for (cls in pkg.allClasses()) { 103 if (!cls.emit) { 104 continue 105 } 106 if (cls.artifact == null) { 107 cls.artifact = mavenSpec 108 } else { 109 reporter.report( 110 Issues.BROKEN_ARTIFACT_FILE, cls, 111 "Class ${cls.qualifiedName()} belongs to multiple artifacts: ${cls.artifact} and $mavenSpec" 112 ) 113 } 114 } 115 } 116 } 117 } 118