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