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