| // 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(); | 
 |   } | 
 | } |