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