• 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.SdkConstants
20 import com.android.tools.metalava.model.AnnotationRetention
21 import com.android.tools.metalava.model.Codebase
22 import java.io.File
23 import kotlin.text.Charsets.UTF_8
24 
25 /**
26  * Converts public stub annotation sources into package private annotation sources.
27  * This is needed for the stub sources, where we want to reference annotations that aren't
28  * public, but (a) they need to be public during compilation, and (b) they need to be
29  * package private when compiled and packaged on their own such that annotation processors
30  * can find them. See b/110532131 for details.
31  */
32 class RewriteAnnotations {
33     /** Modifies annotation source files such that they are package private */
modifyAnnotationSourcesnull34     fun modifyAnnotationSources(codebase: Codebase?, source: File, target: File, pkg: String = "") {
35         val fileName = source.name
36         if (fileName.endsWith(SdkConstants.DOT_JAVA)) {
37             // Only copy non-source retention annotation classes
38             val qualifiedName = pkg + "." + fileName.substring(0, fileName.indexOf('.'))
39             if (hasSourceRetention(codebase, qualifiedName)) {
40                 return
41             }
42 
43             // Copy and convert
44             target.parentFile.mkdirs()
45             target.writeText(
46                 source.readText(UTF_8).replace(
47                     "\npublic @interface",
48                     "\n@interface"
49                 )
50             )
51         } else if (source.isDirectory) {
52             val newPackage = if (pkg.isEmpty()) fileName else "$pkg.$fileName"
53             source.listFiles()?.forEach {
54                 modifyAnnotationSources(codebase, it, File(target, it.name), newPackage)
55             }
56         }
57     }
58 
59     /** Returns true if the given annotation class name has source retention as far as the stub
60      * annotations are concerned.
61      */
hasSourceRetentionnull62     private fun hasSourceRetention(codebase: Codebase?, qualifiedName: String): Boolean {
63         when {
64             qualifiedName == RECENTLY_NULLABLE ||
65                 qualifiedName == RECENTLY_NONNULL ||
66                 qualifiedName == ANDROID_NULLABLE ||
67                 qualifiedName == ANDROID_NONNULL -> return false
68             qualifiedName.equals(ANDROID_SDK_CONSTANT) -> return true
69             qualifiedName.startsWith("androidx.annotation.") -> return true
70         }
71 
72         // See if the annotation is pointing to an annotation class that is part of the API; if not, skip it.
73         if (codebase != null) {
74             val cls = codebase.findClass(qualifiedName) ?: return true
75             return cls.isAnnotationType() && cls.getRetention() == AnnotationRetention.SOURCE
76         } else {
77             error("Found annotation with unknown desired retention: " + qualifiedName)
78         }
79     }
80 }
81