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] */ 34 fun register(artifactId: String, signatureFile: File) { 35 artifacts[signatureFile] = artifactId 36 } 37 38 /** Any registered artifacts? */ 39 fun any() = artifacts.isNotEmpty() 40 41 /** Returns the artifacts */ 42 private fun getRegistrations(): Collection<Map.Entry<File, String>> = artifacts.entries 43 44 /** 45 * Applies the artifact registrations in this map to the given [codebase]. 46 * If [warnAboutMissing] is true, it will complain if any classes in the API 47 * are found that have not been tagged (e.g. where no artifact signature file 48 * referenced the API. 49 */ 50 fun tag(codebase: Codebase, warnAboutMissing: Boolean = true) { 51 if (!any()) { 52 return 53 } 54 55 // Read through the XML files in order, applying their artifact information 56 // to the Javadoc models. 57 for (artifactSpec in getRegistrations()) { 58 val xmlFile = artifactSpec.key 59 val artifactName = artifactSpec.value 60 61 val specApi: TextCodebase 62 try { 63 val kotlinStyleNulls = options.inputKotlinStyleNulls 64 specApi = ApiFile.parseApi(xmlFile, kotlinStyleNulls, false) 65 } catch (e: ApiParseException) { 66 reporter.report( 67 Errors.BROKEN_ARTIFACT_FILE, xmlFile, 68 "Failed to parse $xmlFile for $artifactName artifact data.\n" 69 ) 70 continue 71 } 72 73 applyArtifactsFromSpec(artifactName, specApi, codebase) 74 } 75 76 if (warnAboutMissing) { 77 codebase.accept(object : ApiVisitor(codebase) { 78 override fun visitClass(cls: ClassItem) { 79 if (cls.artifact == null && cls.isTopLevelClass()) { 80 reporter.report( 81 Errors.NO_ARTIFACT_DATA, cls, 82 "No registered artifact signature file referenced class ${cls.qualifiedName()}" 83 ) 84 } 85 } 86 }) 87 } 88 } 89 90 private fun applyArtifactsFromSpec( 91 mavenSpec: String, 92 specApi: TextCodebase, 93 codebase: Codebase 94 ) { 95 for (specPkg in specApi.getPackages().packages) { 96 val pkg = codebase.findPackage(specPkg.qualifiedName()) ?: continue 97 for (cls in pkg.allClasses()) { 98 if (cls.artifact == null) { 99 cls.artifact = mavenSpec 100 } else { 101 reporter.report( 102 Errors.BROKEN_ARTIFACT_FILE, cls, 103 "Class ${cls.qualifiedName()} belongs to multiple artifacts: ${cls.artifact} and $mavenSpec" 104 ) 105 } 106 } 107 } 108 } 109 }