1 /*
2  * Copyright 2020 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 package android.app.appsearch;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.util.ArrayMap;
21 
22 import java.util.Collections;
23 import java.util.Map;
24 import java.util.Objects;
25 
26 /**
27  * Provides results for AppSearch batch operations which encompass multiple documents.
28  *
29  * <p>Individual results of a batch operation are separated into two maps: one for successes and one
30  * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of
31  * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link
32  * AppSearchResult} objects.
33  *
34  * <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for
35  * both successes and failures.
36  *
37  * @param <KeyType> The type of the keys for which the results will be reported.
38  * @param <ValueType> The type of the result objects for successful results.
39  * @see AppSearchSession#put
40  * @see AppSearchSession#getByDocumentId
41  * @see AppSearchSession#remove
42  */
43 public final class AppSearchBatchResult<KeyType, ValueType> {
44     @NonNull private final Map<KeyType, ValueType> mSuccesses;
45     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
46     @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
47 
AppSearchBatchResult( @onNull Map<KeyType, ValueType> successes, @NonNull Map<KeyType, AppSearchResult<ValueType>> failures, @NonNull Map<KeyType, AppSearchResult<ValueType>> all)48     AppSearchBatchResult(
49             @NonNull Map<KeyType, ValueType> successes,
50             @NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
51             @NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
52         mSuccesses = Objects.requireNonNull(successes);
53         mFailures = Objects.requireNonNull(failures);
54         mAll = Objects.requireNonNull(all);
55     }
56 
57     /** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
isSuccess()58     public boolean isSuccess() {
59         return mFailures.isEmpty();
60     }
61 
62     /**
63      * Returns a {@link Map} of keys mapped to instances of the value type for all successful
64      * individual results.
65      *
66      * <p>Example: {@link AppSearchSession#getByDocumentId} returns an {@link AppSearchBatchResult}.
67      * Each key (the document ID, of {@code String} type) will map to a {@link GenericDocument}
68      * object.
69      *
70      * <p>The values of the {@link Map} will not be {@code null}.
71      */
72     @NonNull
getSuccesses()73     public Map<KeyType, ValueType> getSuccesses() {
74         return Collections.unmodifiableMap(mSuccesses);
75     }
76 
77     /**
78      * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed
79      * individual results.
80      *
81      * <p>The values of the {@link Map} will not be {@code null}.
82      */
83     @NonNull
getFailures()84     public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
85         return Collections.unmodifiableMap(mFailures);
86     }
87 
88     /**
89      * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all
90      * individual results.
91      *
92      * <p>The values of the {@link Map} will not be {@code null}.
93      */
94     @NonNull
getAll()95     public Map<KeyType, AppSearchResult<ValueType>> getAll() {
96         return Collections.unmodifiableMap(mAll);
97     }
98 
99     /**
100      * Asserts that this {@link AppSearchBatchResult} has no failures.
101      *
102      * @hide
103      */
checkSuccess()104     public void checkSuccess() {
105         if (!isSuccess()) {
106             throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
107         }
108     }
109 
110     @Override
111     @NonNull
toString()112     public String toString() {
113         return "{\n  successes: " + mSuccesses + "\n  failures: " + mFailures + "\n}";
114     }
115 
116     /**
117      * Builder for {@link AppSearchBatchResult} objects.
118      *
119      * @param <KeyType> The type of the keys for which the results will be reported.
120      * @param <ValueType> The type of the result objects for successful results.
121      */
122     public static final class Builder<KeyType, ValueType> {
123         private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
124         private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
125         private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
126         private boolean mBuilt = false;
127 
128         /**
129          * Associates the {@code key} with the provided successful return value.
130          *
131          * <p>Any previous mapping for a key, whether success or failure, is deleted.
132          *
133          * <p>This is a convenience function which is equivalent to {@code setResult(key,
134          * AppSearchResult.newSuccessfulResult(value))}.
135          *
136          * @param key The key to associate the result with; usually corresponds to some identifier
137          *     from the input like an ID or name.
138          * @param value An optional value to associate with the successful result of the operation
139          *     being performed.
140          */
141         @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
142         @NonNull
setSuccess( @onNull KeyType key, @Nullable ValueType value)143         public Builder<KeyType, ValueType> setSuccess(
144                 @NonNull KeyType key, @Nullable ValueType value) {
145             Objects.requireNonNull(key);
146             resetIfBuilt();
147             return setResult(key, AppSearchResult.newSuccessfulResult(value));
148         }
149 
150         /**
151          * Associates the {@code key} with the provided failure code and error message.
152          *
153          * <p>Any previous mapping for a key, whether success or failure, is deleted.
154          *
155          * <p>This is a convenience function which is equivalent to {@code setResult(key,
156          * AppSearchResult.newFailedResult(resultCode, errorMessage))}.
157          *
158          * @param key The key to associate the result with; usually corresponds to some identifier
159          *     from the input like an ID or name.
160          * @param resultCode One of the constants documented in {@link
161          *     AppSearchResult#getResultCode}.
162          * @param errorMessage An optional string describing the reason or nature of the failure.
163          */
164         @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
165         @NonNull
setFailure( @onNull KeyType key, @AppSearchResult.ResultCode int resultCode, @Nullable String errorMessage)166         public Builder<KeyType, ValueType> setFailure(
167                 @NonNull KeyType key,
168                 @AppSearchResult.ResultCode int resultCode,
169                 @Nullable String errorMessage) {
170             Objects.requireNonNull(key);
171             resetIfBuilt();
172             return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
173         }
174 
175         /**
176          * Associates the {@code key} with the provided {@code result}.
177          *
178          * <p>Any previous mapping for a key, whether success or failure, is deleted.
179          *
180          * @param key The key to associate the result with; usually corresponds to some identifier
181          *     from the input like an ID or name.
182          * @param result The result to associate with the key.
183          */
184         @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
185         @NonNull
setResult( @onNull KeyType key, @NonNull AppSearchResult<ValueType> result)186         public Builder<KeyType, ValueType> setResult(
187                 @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
188             Objects.requireNonNull(key);
189             Objects.requireNonNull(result);
190             resetIfBuilt();
191             if (result.isSuccess()) {
192                 mSuccesses.put(key, result.getResultValue());
193                 mFailures.remove(key);
194             } else {
195                 mFailures.put(key, result);
196                 mSuccesses.remove(key);
197             }
198             mAll.put(key, result);
199             return this;
200         }
201 
202         /**
203          * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}.
204          */
205         @NonNull
build()206         public AppSearchBatchResult<KeyType, ValueType> build() {
207             mBuilt = true;
208             return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
209         }
210 
resetIfBuilt()211         private void resetIfBuilt() {
212             if (mBuilt) {
213                 mSuccesses = new ArrayMap<>(mSuccesses);
214                 mFailures = new ArrayMap<>(mFailures);
215                 mAll = new ArrayMap<>(mAll);
216                 mBuilt = false;
217             }
218         }
219     }
220 }
221