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.appsearch.transformer;
18 
19 import android.annotation.NonNull;
20 import android.app.appsearch.GenericDocument;
21 import android.app.appsearch.SearchResult;
22 import android.app.appsearch.SearchResultPage;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Objects;
29 
30 /** Transforms the retrieved documents in {@link SearchResult} for enterprise access. */
31 public final class EnterpriseSearchResultPageTransformer {
32 
EnterpriseSearchResultPageTransformer()33     private EnterpriseSearchResultPageTransformer() {}
34 
35     /**
36      * Transforms a {@link SearchResultPage}, applying enterprise document transformations in the
37      * {@link SearchResult}s where necessary.
38      */
39     @NonNull
transformSearchResultPage( @onNull SearchResultPage searchResultPage)40     public static SearchResultPage transformSearchResultPage(
41             @NonNull SearchResultPage searchResultPage) {
42         Objects.requireNonNull(searchResultPage);
43         if (!shouldTransformSearchResultPage(searchResultPage)) {
44             return searchResultPage;
45         }
46         List<SearchResult> results = searchResultPage.getResults();
47         List<SearchResult> transformedResults = new ArrayList<>();
48         for (int i = 0; i < results.size(); i++) {
49             transformedResults.add(transformSearchResult(results.get(i)));
50         }
51         return new SearchResultPage(searchResultPage.getNextPageToken(), transformedResults);
52     }
53 
54     /**
55      * Transforms a {@link SearchResult} and nested joined {@link SearchResult}s, applying
56      * enterprise document transformations where necessary.
57      */
58     @VisibleForTesting
59     @NonNull
transformSearchResult(@onNull SearchResult originalResult)60     static SearchResult transformSearchResult(@NonNull SearchResult originalResult) {
61         Objects.requireNonNull(originalResult);
62         boolean shouldTransformDocument =
63                 shouldTransformDocument(
64                         originalResult.getPackageName(),
65                         originalResult.getDatabaseName(),
66                         originalResult.getGenericDocument());
67         boolean shouldTransformJoinedResults =
68                 shouldTransformSearchResults(originalResult.getJoinedResults());
69         // Split the transform check so we can avoid transforming both the original and joined
70         // results when only one actually needs to be transformed.
71         if (!shouldTransformDocument && !shouldTransformJoinedResults) {
72             return originalResult;
73         }
74         SearchResult.Builder builder = new SearchResult.Builder(originalResult);
75         if (shouldTransformDocument) {
76             GenericDocument transformedDocument =
77                     transformDocument(
78                             originalResult.getPackageName(),
79                             originalResult.getDatabaseName(),
80                             originalResult.getGenericDocument());
81             builder.setGenericDocument(transformedDocument);
82         }
83         if (shouldTransformJoinedResults) {
84             List<SearchResult> joinedResults = originalResult.getJoinedResults();
85             builder.clearJoinedResults();
86             for (int i = 0; i < joinedResults.size(); i++) {
87                 SearchResult transformedResult = transformSearchResult(joinedResults.get(i));
88                 builder.addJoinedResult(transformedResult);
89             }
90         }
91         return builder.build();
92     }
93 
94     /**
95      * Transforms the given document specific to its schema type, package, and database or returns
96      * the original document if the combination is not recognized.
97      */
98     @NonNull
transformDocument( @onNull String packageName, @NonNull String databaseName, @NonNull GenericDocument originalDocument)99     public static GenericDocument transformDocument(
100             @NonNull String packageName,
101             @NonNull String databaseName,
102             @NonNull GenericDocument originalDocument) {
103         if (PersonEnterpriseTransformer.shouldTransform(
104                 packageName, databaseName, originalDocument.getSchemaType())) {
105             return PersonEnterpriseTransformer.transformDocument(originalDocument);
106         }
107         return originalDocument;
108     }
109 
110     /** Checks if we need to transform the {@link SearchResultPage}. */
shouldTransformSearchResultPage( @onNull SearchResultPage searchResultPage)111     private static boolean shouldTransformSearchResultPage(
112             @NonNull SearchResultPage searchResultPage) {
113         List<SearchResult> results = searchResultPage.getResults();
114         for (int i = 0; i < results.size(); i++) {
115             if (shouldTransformSearchResult(results.get(i))) {
116                 return true;
117             }
118         }
119         return false;
120     }
121 
122     /** Checks if we need to transform the {@link SearchResult}. */
shouldTransformSearchResult(@onNull SearchResult searchResult)123     private static boolean shouldTransformSearchResult(@NonNull SearchResult searchResult) {
124         return shouldTransformDocument(
125                         searchResult.getPackageName(),
126                         searchResult.getDatabaseName(),
127                         searchResult.getGenericDocument())
128                 || shouldTransformSearchResults(searchResult.getJoinedResults());
129     }
130 
131     /** Checks if we need to transform the {@link SearchResult}s. */
shouldTransformSearchResults(@onNull List<SearchResult> searchResults)132     private static boolean shouldTransformSearchResults(@NonNull List<SearchResult> searchResults) {
133         for (int i = 0; i < searchResults.size(); i++) {
134             if (shouldTransformSearchResult(searchResults.get(i))) {
135                 return true;
136             }
137         }
138         return false;
139     }
140 
141     /** Checks if we need to transform the {@link GenericDocument}. */
shouldTransformDocument( @onNull String packageName, @NonNull String databaseName, @NonNull GenericDocument document)142     private static boolean shouldTransformDocument(
143             @NonNull String packageName,
144             @NonNull String databaseName,
145             @NonNull GenericDocument document) {
146         return PersonEnterpriseTransformer.shouldTransform(
147                 packageName, databaseName, document.getSchemaType());
148     }
149 }
150