Add DAL parsing static lib for StatementService.

Bug: 307557449
Test: atest StatementServiceTests
Flag: EXEMPT external library
Change-Id: If46de807e4442de69de4dfc694099c4baa2b8d0a
diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp
new file mode 100644
index 0000000..c8af134
--- /dev/null
+++ b/packages/StatementService/Parser/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "StatementServiceParser",
+    use_resource_processor: true,
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    target_sdk_version: "29",
+}
diff --git a/packages/StatementService/Parser/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml
new file mode 100644
index 0000000..a3a99ac
--- /dev/null
+++ b/packages/StatementService/Parser/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.statementservice.parser">
+</manifest>
\ No newline at end of file
diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
new file mode 100644
index 0000000..4314f86
--- /dev/null
+++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DalComponentParser")
+
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+
+/**
+ * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type
+ * and pattern. Matching expressions support the following wildcards:
+ *
+ *  1) An asterisk (*) matches zero to as many characters as possible
+ *  2) A question mark (?) matches any single character.
+ *
+ * Matching one to many characters can be done with a question mark followed by an asterisk (?+).
+ *
+ * @param expression A matching expression string from a DAL relation extension component used for
+ *                   matching a URI part. This must be a non-empty string and all characters in the
+ *                   string should be decoded.
+ *
+ * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern.
+ */
+fun parseMatchingExpression(expression: String): Pair<Int, String> {
+    if (expression.isNullOrEmpty()) {
+        throw IllegalArgumentException("Matching expressions cannot be an empty string")
+    }
+    var count = 0
+    var isAdvanced = expression.contains("?*")
+    val pattern = buildString {
+        for (char in expression) {
+            when (char) {
+                '*' -> {
+                    if (this.endsWith('.') && !this.endsWith("\\.")) {
+                        append('+')
+                    } else {
+                        count += 1
+                        append(".*")
+                    }
+                }
+                '?' -> {
+                    count += 1
+                    append('.')
+                }
+                '.' -> {
+                    append("\\.")
+                }
+                '[', ']', '{', '}' -> {
+                    if (isAdvanced) {
+                        append('\\')
+                    }
+                    append(char)
+                }
+                else -> append(char)
+            }
+        }
+    }
+    if (count == 0) {
+        return Pair(PATTERN_LITERAL, pattern)
+    }
+    if (count == 1 && pattern.endsWith(".*")) {
+        return Pair(PATTERN_PREFIX, pattern.dropLast(2))
+    }
+    if (isAdvanced) {
+        return Pair(PATTERN_ADVANCED_GLOB, pattern)
+    }
+    return Pair(PATTERN_SIMPLE_GLOB, pattern)
+}
\ No newline at end of file
diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING
new file mode 100644
index 0000000..0714c93
--- /dev/null
+++ b/packages/StatementService/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit" : [
+    {
+      "name": "StatementServiceTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp
new file mode 100644
index 0000000..ec1bd96
--- /dev/null
+++ b/packages/StatementService/tests/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_framework_android_packages",
+}
+
+android_test {
+    name: "StatementServiceTests",
+    use_resource_processor: true,
+    test_suites: ["general-tests"],
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "StatementServiceParser",
+        "truth",
+    ],
+}
diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml
new file mode 100644
index 0000000..bde0f95
--- /dev/null
+++ b/packages/StatementService/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.statementservice.test">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.statementservice.test" />
+</manifest>
diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
new file mode 100644
index 0000000..44a56ec
--- /dev/null
+++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DalComponentParserTest {
+
+    @Test
+    fun parseExpressions() {
+        validateParsedExpression("foobar", PATTERN_LITERAL, "foobar")
+        validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar")
+        validateParsedExpression("foo*", PATTERN_PREFIX, "foo")
+        validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar")
+        validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar")
+        validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar")
+        validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar")
+        validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar")
+        validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar")
+        validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar")
+        validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.")
+        validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r")
+        validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar")
+        validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar")
+        validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*")
+        validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar")
+
+        // set matches are not supported in DAL
+        validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]")
+        validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+")
+        validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]")
+        validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar")
+        validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar")
+        validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar")
+
+        // range matches are not supported in DAL
+        validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}")
+        validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+")
+        validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}")
+        validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar")
+        validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+")
+        validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar")
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun parseEmptyExpression() {
+        parseMatchingExpression("")
+    }
+
+    private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) {
+        val (type, filter) = parseMatchingExpression(given)
+        assertThat(filter).isEqualTo(expectedFilter)
+        assertThat(type).isEqualTo(expectedType)
+        assertThat(filter).isEqualTo(expectedFilter)
+    }
+}
\ No newline at end of file