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