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.model 18 19 import java.io.File 20 import java.util.function.Predicate 21 22 /** 23 * Checks to see if a package name matches a set of configured rules. 24 * 25 * This supports a number of rule styles: 26 * - exact match (foo) 27 * - prefix match (foo*, probably not intentional) 28 * - package and subpackage match (foo.*) 29 * - explicit addition (+foo.*) 30 * - subtraction (+*:-foo.*) 31 * 32 * Real examples: args: "-stubpackages com.android.test.power ", args: "-stubpackages android.car* 33 * ", args: "-stubpackages com.android.ahat:com.android.ahat.*", args: 34 * "-force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* 35 */ 36 class PackageFilter { 37 private val components: MutableList<PackageFilterComponent> = mutableListOf() 38 matchesnull39 fun matches(qualifiedName: String): Boolean { 40 for (component in components.reversed()) { 41 if (component.filter.test(qualifiedName)) { 42 return component.treatAsPositiveMatch 43 } 44 } 45 return false 46 } 47 addPackagesnull48 internal fun addPackages(path: String) { 49 for (arg in path.split(File.pathSeparatorChar)) { 50 val treatAsPositiveMatch = !arg.startsWith("-") 51 val pkg = arg.removePrefix("-").removePrefix("+") 52 val index = pkg.indexOf('*') 53 if (index != -1) { 54 if (index < pkg.length - 1) { 55 throw IllegalStateException( 56 "Wildcards in stub packages must be at the end of the package: $pkg" 57 ) 58 } 59 val prefix = pkg.removeSuffix("*") 60 if (prefix.endsWith(".")) { 61 // In doclava, "foo.*" does not match "foo", but we want to do that. 62 val exact = prefix.substring(0, prefix.length - 1) 63 add(StringEqualsPredicate(exact), treatAsPositiveMatch) 64 } 65 add(StringPrefixPredicate(prefix), treatAsPositiveMatch) 66 } else { 67 add(StringEqualsPredicate(pkg), treatAsPositiveMatch) 68 } 69 } 70 } 71 addnull72 private fun add(predicate: Predicate<String>, treatAsPositiveMatch: Boolean) { 73 components.add(PackageFilterComponent(predicate, treatAsPositiveMatch)) 74 } 75 matchesnull76 fun matches(packageItem: PackageItem): Boolean { 77 return matches(packageItem.qualifiedName()) 78 } 79 80 companion object { parsenull81 fun parse(path: String): PackageFilter { 82 val filter = PackageFilter() 83 filter.addPackages(path) 84 return filter 85 } 86 } 87 } 88 89 internal class StringPrefixPredicate(private val acceptedPrefix: String) : Predicate<String> { testnull90 override fun test(candidatePackage: String): Boolean { 91 return candidatePackage.startsWith(acceptedPrefix) 92 } 93 } 94 95 internal class StringEqualsPredicate(private val acceptedPackage: String) : Predicate<String> { testnull96 override fun test(candidatePackage: String): Boolean { 97 return candidatePackage == acceptedPackage 98 } 99 } 100 101 /** 102 * One element of a PackageFilter. Detects packages and either includes or excludes them from the 103 * filter 104 */ 105 internal class PackageFilterComponent( 106 val filter: Predicate<String>, 107 val treatAsPositiveMatch: Boolean, 108 ) 109