|  | // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | package com.android.tools.r8.shaking; | 
|  |  | 
|  | import com.android.tools.r8.graph.DexType; | 
|  |  | 
|  | public class ProguardPackageMatcher { | 
|  | private final String pattern; | 
|  |  | 
|  | public ProguardPackageMatcher(String pattern) { | 
|  | this.pattern = pattern; | 
|  | } | 
|  |  | 
|  | public boolean matches(DexType type) { | 
|  | return matchPackageNameImpl(pattern, 0, type.getPackageName(), 0); | 
|  | } | 
|  |  | 
|  | private static boolean matchPackageNameImpl( | 
|  | String pattern, int patternIndex, String name, int nameIndex) { | 
|  | for (int i = patternIndex; i < pattern.length(); i++) { | 
|  | char patternChar = pattern.charAt(i); | 
|  | switch (patternChar) { | 
|  | case '*': | 
|  | int nextPatternIndex = i + 1; | 
|  | // Check for **. | 
|  | boolean includeSeparators = | 
|  | pattern.length() > (nextPatternIndex) && pattern.charAt(nextPatternIndex) == '*'; | 
|  | if (includeSeparators) { | 
|  | nextPatternIndex += 1; | 
|  | } | 
|  |  | 
|  | // Fast cases for the common case where a pattern ends with '*' or '**'. | 
|  | if (nextPatternIndex == pattern.length()) { | 
|  | if (includeSeparators) { | 
|  | return true; | 
|  | } | 
|  | boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex); | 
|  | return !hasSeparators; | 
|  | } | 
|  |  | 
|  | // Match the rest of the pattern against the (non-empty) rest of the class name. | 
|  | for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) { | 
|  | if (!includeSeparators) { | 
|  | // Stop at the first separator for just *. | 
|  | if (name.charAt(nextNameIndex) == '.') { | 
|  | return matchPackageNameImpl(pattern, nextPatternIndex, name, nextNameIndex); | 
|  | } | 
|  | } | 
|  | if (matchPackageNameImpl(pattern, nextPatternIndex, name, nextNameIndex)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Finally, check the case where the '*' or '**' eats all of the package name. | 
|  | return matchPackageNameImpl(pattern, nextPatternIndex, name, name.length()); | 
|  |  | 
|  | case '?': | 
|  | if (nameIndex == name.length() || name.charAt(nameIndex) == '.') { | 
|  | return false; | 
|  | } | 
|  | nameIndex++; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) { | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | return nameIndex == name.length(); | 
|  | } | 
|  |  | 
|  | private static boolean containsSeparatorsStartingAt(String className, int nameIndex) { | 
|  | return className.indexOf('.', nameIndex) != -1; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object o) { | 
|  | if (this == o) { | 
|  | return true; | 
|  | } | 
|  | if (!(o instanceof ProguardPackageMatcher)) { | 
|  | return false; | 
|  | } | 
|  | ProguardPackageMatcher other = (ProguardPackageMatcher) o; | 
|  | return pattern.equals(other.pattern); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return pattern.hashCode(); | 
|  | } | 
|  | } |