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 }