1 package com.android.tools.metalava.doclava1 2 3 import com.android.tools.metalava.Options 4 import com.android.tools.metalava.model.ClassItem 5 import com.android.tools.metalava.model.Item 6 import com.android.tools.metalava.model.MemberItem 7 import com.android.tools.metalava.model.PackageItem 8 import com.android.tools.metalava.options 9 import java.util.function.Predicate 10 11 // Ported from doclava1 12 13 /** 14 * Predicate that decides if the given member should be considered part of an 15 * API surface area. To make the most accurate decision, it searches for 16 * signals on the member, all containing classes, and all containing packages. 17 */ 18 class ApiPredicate( 19 /** 20 * Set if the value of [MemberItem.hasShowAnnotation] should be 21 * ignored. That is, this predicate will assume that all encountered members 22 * match the "shown" requirement. 23 * 24 * This is typically useful when generating "current.txt", when no 25 * [Options.showAnnotations] have been defined. 26 */ 27 val ignoreShown: Boolean = options.showUnannotated, 28 29 /** 30 * Set if the value of [MemberItem.removed] should be ignored. 31 * That is, this predicate will assume that all encountered members match 32 * the "removed" requirement. 33 * 34 * This is typically useful when generating "removed.txt", when it's okay to 35 * reference both current and removed APIs. 36 */ 37 private val ignoreRemoved: Boolean = false, 38 39 /** 40 * Set what the value of [MemberItem.removed] must be equal to in 41 * order for a member to match. 42 * 43 * This is typically useful when generating "removed.txt", when you only 44 * want to match members that have actually been removed. 45 */ 46 private val matchRemoved: Boolean = false, 47 48 /** Whether we allow matching items loaded from jar files instead of sources */ 49 private val allowClassesFromClasspath: Boolean = options.allowClassesFromClasspath, 50 51 /** Whether we should include doc-only items */ 52 private val includeDocOnly: Boolean = false, 53 54 /** Whether to include "for stub purposes" APIs. See [Options.showForStubPurposesAnnotations] */ 55 private val includeApisForStubPurposes: Boolean = true 56 ) : Predicate<Item> { 57 testnull58 override fun test(member: Item): Boolean { 59 // Type Parameter references (e.g. T) aren't actual types, skip all visibility checks 60 if (member is ClassItem && member.isTypeParameter) { 61 return true 62 } 63 64 if (!allowClassesFromClasspath && member.isFromClassPath()) { 65 return false 66 } 67 68 var visible = member.isPublic || member.isProtected || (member.isInternal && member.hasShowAnnotation()) // TODO: Should this use checkLevel instead? 69 var hidden = member.hidden 70 if (!visible || hidden) { 71 return false 72 } 73 if (!includeApisForStubPurposes && includeOnlyForStubPurposes(member)) { 74 return false 75 } 76 77 var hasShowAnnotation = ignoreShown || member.hasShowAnnotation() 78 var docOnly = member.docOnly 79 var removed = member.removed 80 81 var clazz: ClassItem? = when (member) { 82 is MemberItem -> member.containingClass() 83 is ClassItem -> member 84 else -> null 85 } 86 87 if (clazz != null) { 88 var pkg: PackageItem? = clazz.containingPackage() 89 while (pkg != null) { 90 hidden = hidden or pkg.hidden 91 docOnly = docOnly or pkg.docOnly 92 removed = removed or pkg.removed 93 pkg = pkg.containingPackage() 94 } 95 } 96 while (clazz != null) { 97 visible = visible and (clazz.isPublic || clazz.isProtected || 98 (clazz.isInternal && clazz.hasShowAnnotation()) 99 ) 100 hasShowAnnotation = hasShowAnnotation or (ignoreShown || clazz.hasShowAnnotation()) 101 hidden = hidden or clazz.hidden 102 docOnly = docOnly or clazz.docOnly 103 removed = removed or clazz.removed 104 clazz = clazz.containingClass() 105 } 106 107 if (ignoreRemoved) { 108 removed = matchRemoved 109 } 110 111 if (docOnly && includeDocOnly) { 112 docOnly = false 113 } 114 115 return visible && hasShowAnnotation && !hidden && !docOnly && removed == matchRemoved 116 } 117 118 /** 119 * Returns true, if an item should be included only for "stub" purposes; that is, 120 * the item does *not* have a [Options.showAnnotations] annotation but 121 * has a [Options.showForStubPurposesAnnotations] annotation. 122 */ includeOnlyForStubPurposesnull123 private fun includeOnlyForStubPurposes(item: Item): Boolean { 124 if (options.showForStubPurposesAnnotations.isEmpty()) { 125 return false 126 } 127 128 // If the item has a "show" annotation, then return whether it has a "for stubs" annotation 129 // or not. 130 // 131 // Note, If the item does not have a show annotation, then it can't have a "for stubs" one, 132 // because the later must be a subset of the former, which we don't detect in *this* 133 // run (unfortunately it's hard to do so due to how things work), but when metalava 134 // is executed for the parent API, we'd detect it as 135 // [Issues.SHOWING_MEMBER_IN_HIDDEN_CLASS]. 136 if (item.hasShowAnnotationInherited()) { 137 return item.hasShowForStubPurposesAnnotationInherited() 138 } 139 // If this item has neither --show-annotation nor --parent-api-annotation, 140 // Then defer to the "parent" item (i.e. the enclosing class or package). 141 return item.parent()?.let { includeOnlyForStubPurposes(it) } ?: false 142 } 143 } 144