1 /* 2 * Copyright (C) 2023 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.server.sdksandbox.helpers; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.util.ArraySet; 22 23 /** 24 * Helper class for String operations. 25 * 26 * @hide 27 */ 28 public class StringHelper { 29 /** 30 * Checks if a given input string matches any of the given patterns. Each pattern can contain 31 * wildcards in the form of an asterisk. This wildcard should match 0 or more number of 32 * characters in the input string. 33 */ doesInputMatchAnyWildcardPattern( @onNull ArraySet<String> patterns, @Nullable String input)34 public static boolean doesInputMatchAnyWildcardPattern( 35 @NonNull ArraySet<String> patterns, @Nullable String input) { 36 for (int i = 0; i < patterns.size(); ++i) { 37 if (doesInputMatchWildcardPattern( 38 patterns.valueAt(i), input, /*matchOnNullInput=*/ false)) { 39 return true; 40 } 41 } 42 return false; 43 } 44 45 /** 46 * Checks if a given input string matches the given pattern. The pattern can contain wildcards 47 * in the form of an asterisk. This wildcard should match 0 or more number of characters in the 48 * input string. 49 */ doesInputMatchWildcardPattern( @ullable String pattern, @Nullable String input, boolean matchOnNullInput)50 public static boolean doesInputMatchWildcardPattern( 51 @Nullable String pattern, @Nullable String input, boolean matchOnNullInput) { 52 if (matchOnNullInput && (pattern != null && pattern.equals("*"))) { 53 return true; 54 } 55 if (pattern == null || input == null) { 56 return false; 57 } 58 59 /* 60 * We split the pattern by the wildcard. It is split with a non-negative limit, indicating 61 * that the pattern is applied as many times as possible e.g. if pattern = "*a*", the split 62 * would be ["","a",""]. 63 */ 64 // TODO(b/289197372): Optimize by splitting beforehand. 65 String[] patternSubstrings = pattern.split("\\*", -1); 66 int inputMatchStartIndex = 0; 67 for (int i = 0; i < patternSubstrings.length; ++i) { 68 if (i == 0) { 69 // Verify that the input string starts with the characters present before the first 70 // wildcard. 71 if (!input.startsWith(patternSubstrings[i])) { 72 return false; 73 } 74 inputMatchStartIndex = patternSubstrings[i].length(); 75 } else if (i == patternSubstrings.length - 1) { 76 // Verify that the input string (after the point where it's been matched so far) 77 // matches with the characters after the last wildcard. 78 if (!input.substring(inputMatchStartIndex).endsWith(patternSubstrings[i])) { 79 return false; 80 } 81 inputMatchStartIndex = input.length(); 82 } else { 83 // For patterns between the first and last wildcard, greedily check if the input 84 // (after the point where it's been matched so far) matches properly. 85 int substringIndex = input.indexOf(patternSubstrings[i], inputMatchStartIndex); 86 if (substringIndex == -1) { 87 return false; 88 } 89 inputMatchStartIndex = substringIndex + patternSubstrings[i].length(); 90 } 91 } 92 93 // Verify that the whole input has been matched. 94 return inputMatchStartIndex >= input.length(); 95 } 96 } 97