1 package com.android.tools.metalava 2 3 import com.android.tools.metalava.model.PackageItem 4 import java.io.File 5 import java.util.function.Predicate 6 7 /** 8 * 9 * We permit a number of different styles: 10 * - exact match (foo) 11 * - prefix match (foo*, probably not intentional) 12 * - subpackage match (foo.*) 13 * - package and subpackage match (foo:foo.*) 14 * - explicit addition (+foo.*) 15 * - subtraction (+*:-foo.*) 16 * 17 * Real examples: 18 * args: "-stubpackages com.android.test.power ", 19 * args: "-stubpackages android.car* ", 20 * args: "-stubpackages com.android.ahat:com.android.ahat.*", 21 * args: "-force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* 22 * 23 * Note that doclava does *not* include subpackages by default: -stubpackage foo 24 * will match only foo, not foo.bar. Note also that "foo.*" will not match "foo", 25 * so doclava required you to supply both: "foo:foo.*". 26 * 27 * In metalava we've changed that: it's not likely that you want to 28 * match any subpackage of foo but not foo itself, so foo.* is taken 29 * to mean "foo" and "foo.*". 30 */ 31 class PackageFilter { 32 val components: MutableList<PackageFilterComponent> = mutableListOf() 33 matchesnull34 fun matches(qualifiedName: String): Boolean { 35 for (component in components.reversed()) { 36 if (component.filter.test(qualifiedName)) { 37 return component.treatAsPositiveMatch 38 } 39 } 40 return false 41 } 42 addPackagesnull43 fun addPackages(path: String) { 44 for (arg in path.split(File.pathSeparatorChar)) { 45 val treatAsPositiveMatch = !arg.startsWith("-") 46 val pkg = arg.removePrefix("-").removePrefix("+") 47 val index = pkg.indexOf('*') 48 if (index != -1) { 49 if (index < pkg.length - 1) { 50 throw DriverException(stderr = "Wildcards in stub packages must be at the end of the package: $pkg)") 51 } 52 val prefix = pkg.removeSuffix("*") 53 if (prefix.endsWith(".")) { 54 // In doclava, "foo.*" does not match "foo", but we want to do that. 55 val exact = prefix.substring(0, prefix.length - 1) 56 add(StringEqualsPredicate(exact), treatAsPositiveMatch) 57 } 58 add(StringPrefixPredicate(prefix), treatAsPositiveMatch) 59 } else { 60 add(StringEqualsPredicate(pkg), treatAsPositiveMatch) 61 } 62 } 63 } 64 addnull65 fun add(predicate: Predicate<String>, treatAsPositiveMatch: Boolean) { 66 components.add(PackageFilterComponent(predicate, treatAsPositiveMatch)) 67 } 68 matchesnull69 fun matches(packageItem: PackageItem): Boolean { 70 return matches(packageItem.qualifiedName()) 71 } 72 73 companion object { parsenull74 fun parse(path: String): PackageFilter { 75 val filter = PackageFilter() 76 filter.addPackages(path) 77 return filter 78 } 79 } 80 } 81 82 class StringPrefixPredicate(private val acceptedPrefix: String) : Predicate<String> { testnull83 override fun test(candidatePackage: String): Boolean { 84 return candidatePackage.startsWith(acceptedPrefix) 85 } 86 } 87 88 class StringEqualsPredicate(val acceptedPackage: String) : Predicate<String> { testnull89 override fun test(candidatePackage: String): Boolean { 90 return candidatePackage == acceptedPackage 91 } 92 } 93 94 /** 95 * One element of a PackageFilter. 96 * Detects packages and either either includes or excludes them from the filter 97 */ 98 class PackageFilterComponent(val filter: Predicate<String>, val treatAsPositiveMatch: Boolean) 99