1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 @file:JvmName("DalComponentParser")
18
19 package com.android.statementservice.parser
20
21 import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
22 import android.os.PatternMatcher.PATTERN_LITERAL
23 import android.os.PatternMatcher.PATTERN_PREFIX
24 import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
25
26 /**
27 * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type
28 * and pattern. Matching expressions support the following wildcards:
29 *
30 * 1) An asterisk (*) matches zero to as many characters as possible
31 * 2) A question mark (?) matches any single character.
32 *
33 * Matching one to many characters can be done with a question mark followed by an asterisk (?+).
34 *
35 * @param expression A matching expression string from a DAL relation extension component used for
36 * matching a URI part. This must be a non-empty string and all characters in the
37 * string should be decoded.
38 *
39 * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern.
40 */
parseMatchingExpressionnull41 fun parseMatchingExpression(expression: String): Pair<Int, String> {
42 if (expression.isNullOrEmpty()) {
43 throw IllegalArgumentException("Matching expressions cannot be an empty string")
44 }
45 var count = 0
46 var isAdvanced = expression.contains("?*")
47 val pattern = buildString {
48 for (char in expression) {
49 when (char) {
50 '*' -> {
51 if (this.endsWith('.') && !this.endsWith("\\.")) {
52 append('+')
53 } else {
54 count += 1
55 append(".*")
56 }
57 }
58 '?' -> {
59 count += 1
60 append('.')
61 }
62 '.' -> {
63 append("\\.")
64 }
65 '[', ']', '{', '}' -> {
66 if (isAdvanced) {
67 append('\\')
68 }
69 append(char)
70 }
71 else -> append(char)
72 }
73 }
74 }
75 if (count == 0) {
76 return Pair(PATTERN_LITERAL, pattern)
77 }
78 if (count == 1 && pattern.endsWith(".*")) {
79 return Pair(PATTERN_PREFIX, pattern.dropLast(2))
80 }
81 if (isAdvanced) {
82 return Pair(PATTERN_ADVANCED_GLOB, pattern)
83 }
84 return Pair(PATTERN_SIMPLE_GLOB, pattern)
85 }