1 /* 2 * Copyright 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 androidx.appsearch.safeparcel; 18 19 20 import android.annotation.SuppressLint; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 import androidx.annotation.OptIn; 25 import androidx.annotation.RestrictTo; 26 import androidx.appsearch.annotation.CanIgnoreReturnValue; 27 import androidx.appsearch.app.AppSearchBlobHandle; 28 import androidx.appsearch.app.EmbeddingVector; 29 import androidx.appsearch.app.ExperimentalAppSearchApi; 30 31 import org.jspecify.annotations.NonNull; 32 import org.jspecify.annotations.Nullable; 33 34 import java.util.Arrays; 35 import java.util.Objects; 36 37 /** 38 * A {@link SafeParcelable} to hold the value of a property in {@code GenericDocument#mProperties}. 39 * 40 * <p>This resembles PropertyProto in IcingLib. 41 * 42 * @exportToFramework:hide 43 */ 44 @RestrictTo(RestrictTo.Scope.LIBRARY) 45 @SafeParcelable.Class(creator = "PropertyParcelCreator") 46 // This won't be used to send data over binder, and we have to use Parcelable for code sync purpose. 47 @SuppressLint("BanParcelableUsage") 48 public final class PropertyParcel extends AbstractSafeParcelable implements Parcelable { 49 public static final Parcelable.@NonNull Creator<PropertyParcel> CREATOR = 50 new PropertyParcelCreator(); 51 52 @Field(id = 1, getter = "getPropertyName") 53 private final @NonNull String mPropertyName; 54 55 @Field(id = 2, getter = "getStringValues") 56 private final String @Nullable [] mStringValues; 57 58 @Field(id = 3, getter = "getLongValues") 59 private final long @Nullable [] mLongValues; 60 61 @Field(id = 4, getter = "getDoubleValues") 62 private final double @Nullable [] mDoubleValues; 63 64 @Field(id = 5, getter = "getBooleanValues") 65 private final boolean @Nullable [] mBooleanValues; 66 67 @Field(id = 6, getter = "getBytesValues") 68 private final byte @Nullable [][] mBytesValues; 69 70 @Field(id = 7, getter = "getDocumentValues") 71 private final GenericDocumentParcel @Nullable [] mDocumentValues; 72 73 @Field(id = 8, getter = "getEmbeddingValues") 74 private final EmbeddingVector @Nullable [] mEmbeddingValues; 75 76 @Field(id = 9, getter = "getBlobHandleValues") 77 @ExperimentalAppSearchApi 78 private final AppSearchBlobHandle @Nullable [] mBlobHandleValues; 79 80 private @Nullable Integer mHashCode; 81 82 @Constructor 83 @OptIn(markerClass = ExperimentalAppSearchApi.class) PropertyParcel( @aramid = 1) @onNull String propertyName, @Param(id = 2) String @Nullable [] stringValues, @Param(id = 3) long @Nullable [] longValues, @Param(id = 4) double @Nullable [] doubleValues, @Param(id = 5) boolean @Nullable [] booleanValues, @Param(id = 6) byte @Nullable [][] bytesValues, @Param(id = 7) GenericDocumentParcel @Nullable [] documentValues, @Param(id = 8) EmbeddingVector @Nullable [] embeddingValues, @Param(id = 9) AppSearchBlobHandle @Nullable [] blobHandleValues)84 PropertyParcel( 85 @Param(id = 1) @NonNull String propertyName, 86 @Param(id = 2) String @Nullable [] stringValues, 87 @Param(id = 3) long @Nullable [] longValues, 88 @Param(id = 4) double @Nullable [] doubleValues, 89 @Param(id = 5) boolean @Nullable [] booleanValues, 90 @Param(id = 6) byte @Nullable [][] bytesValues, 91 @Param(id = 7) GenericDocumentParcel @Nullable [] documentValues, 92 @Param(id = 8) EmbeddingVector @Nullable [] embeddingValues, 93 @Param(id = 9) AppSearchBlobHandle @Nullable [] blobHandleValues) { 94 mPropertyName = Objects.requireNonNull(propertyName); 95 mStringValues = stringValues; 96 mLongValues = longValues; 97 mDoubleValues = doubleValues; 98 mBooleanValues = booleanValues; 99 mBytesValues = bytesValues; 100 mDocumentValues = documentValues; 101 mEmbeddingValues = embeddingValues; 102 mBlobHandleValues = blobHandleValues; 103 checkOnlyOneArrayCanBeSet(); 104 } 105 106 /** Returns the name of the property. */ getPropertyName()107 public @NonNull String getPropertyName() { 108 return mPropertyName; 109 } 110 111 /** Returns {@code String} values in an array. */ getStringValues()112 public String @Nullable [] getStringValues() { 113 return mStringValues; 114 } 115 116 /** Returns {@code long} values in an array. */ getLongValues()117 public long @Nullable [] getLongValues() { 118 return mLongValues; 119 } 120 121 /** Returns {@code double} values in an array. */ getDoubleValues()122 public double @Nullable [] getDoubleValues() { 123 return mDoubleValues; 124 } 125 126 /** Returns {@code boolean} values in an array. */ getBooleanValues()127 public boolean @Nullable [] getBooleanValues() { 128 return mBooleanValues; 129 } 130 131 /** Returns a two-dimension {@code byte} array. */ getBytesValues()132 public byte @Nullable [][] getBytesValues() { 133 return mBytesValues; 134 } 135 136 /** Returns {@link GenericDocumentParcel}s in an array. */ getDocumentValues()137 public GenericDocumentParcel @Nullable [] getDocumentValues() { 138 return mDocumentValues; 139 } 140 141 /** Returns {@link EmbeddingVector}s in an array. */ getEmbeddingValues()142 public EmbeddingVector @Nullable [] getEmbeddingValues() { 143 return mEmbeddingValues; 144 } 145 146 /** Returns {@link AppSearchBlobHandle}s in an array. */ 147 @OptIn(markerClass = ExperimentalAppSearchApi.class) getBlobHandleValues()148 public AppSearchBlobHandle @Nullable [] getBlobHandleValues() { 149 return mBlobHandleValues; 150 } 151 152 /** 153 * Returns the held values in an array for this property. 154 * 155 * <p>Different from other getter methods, this one will return an {@link Object}. 156 */ 157 @OptIn(markerClass = ExperimentalAppSearchApi.class) getValues()158 public @Nullable Object getValues() { 159 if (mStringValues != null) { 160 return mStringValues; 161 } 162 if (mLongValues != null) { 163 return mLongValues; 164 } 165 if (mDoubleValues != null) { 166 return mDoubleValues; 167 } 168 if (mBooleanValues != null) { 169 return mBooleanValues; 170 } 171 if (mBytesValues != null) { 172 return mBytesValues; 173 } 174 if (mDocumentValues != null) { 175 return mDocumentValues; 176 } 177 if (mEmbeddingValues != null) { 178 return mEmbeddingValues; 179 } 180 if (mBlobHandleValues != null) { 181 return mBlobHandleValues; 182 } 183 return null; 184 } 185 186 /** 187 * Checks there is one and only one array can be set for the property. 188 * 189 * @throws IllegalArgumentException if 0, or more than 1 arrays are set. 190 */ 191 @OptIn(markerClass = ExperimentalAppSearchApi.class) checkOnlyOneArrayCanBeSet()192 private void checkOnlyOneArrayCanBeSet() { 193 int notNullCount = 0; 194 if (mStringValues != null) { 195 ++notNullCount; 196 } 197 if (mLongValues != null) { 198 ++notNullCount; 199 } 200 if (mDoubleValues != null) { 201 ++notNullCount; 202 } 203 if (mBooleanValues != null) { 204 ++notNullCount; 205 } 206 if (mBytesValues != null) { 207 ++notNullCount; 208 } 209 if (mDocumentValues != null) { 210 ++notNullCount; 211 } 212 if (mEmbeddingValues != null) { 213 ++notNullCount; 214 } 215 if (mBlobHandleValues != null) { 216 ++notNullCount; 217 } 218 if (notNullCount == 0 || notNullCount > 1) { 219 throw new IllegalArgumentException( 220 "One and only one type array can be set in PropertyParcel"); 221 } 222 } 223 224 @Override 225 @OptIn(markerClass = ExperimentalAppSearchApi.class) hashCode()226 public int hashCode() { 227 if (mHashCode == null) { 228 int hashCode = 0; 229 if (mStringValues != null) { 230 hashCode = Arrays.hashCode(mStringValues); 231 } else if (mLongValues != null) { 232 hashCode = Arrays.hashCode(mLongValues); 233 } else if (mDoubleValues != null) { 234 hashCode = Arrays.hashCode(mDoubleValues); 235 } else if (mBooleanValues != null) { 236 hashCode = Arrays.hashCode(mBooleanValues); 237 } else if (mBytesValues != null) { 238 hashCode = Arrays.deepHashCode(mBytesValues); 239 } else if (mDocumentValues != null) { 240 hashCode = Arrays.hashCode(mDocumentValues); 241 } else if (mEmbeddingValues != null) { 242 hashCode = Arrays.deepHashCode(mEmbeddingValues); 243 } else if (mBlobHandleValues != null) { 244 hashCode = Arrays.deepHashCode(mBlobHandleValues); 245 } 246 mHashCode = Objects.hash(mPropertyName, hashCode); 247 } 248 return mHashCode; 249 } 250 251 @Override 252 @OptIn(markerClass = ExperimentalAppSearchApi.class) equals(@ullable Object other)253 public boolean equals(@Nullable Object other) { 254 if (this == other) { 255 return true; 256 } 257 if (!(other instanceof PropertyParcel)) { 258 return false; 259 } 260 PropertyParcel otherPropertyParcel = (PropertyParcel) other; 261 if (!mPropertyName.equals(otherPropertyParcel.mPropertyName)) { 262 return false; 263 } 264 return Arrays.equals(mStringValues, otherPropertyParcel.mStringValues) 265 && Arrays.equals(mLongValues, otherPropertyParcel.mLongValues) 266 && Arrays.equals(mDoubleValues, otherPropertyParcel.mDoubleValues) 267 && Arrays.equals(mBooleanValues, otherPropertyParcel.mBooleanValues) 268 && Arrays.deepEquals(mBytesValues, otherPropertyParcel.mBytesValues) 269 && Arrays.equals(mDocumentValues, otherPropertyParcel.mDocumentValues) 270 && Arrays.deepEquals(mEmbeddingValues, otherPropertyParcel.mEmbeddingValues) 271 && Arrays.deepEquals(mBlobHandleValues, otherPropertyParcel.mBlobHandleValues); 272 } 273 274 @Override writeToParcel(@onNull Parcel dest, int flags)275 public void writeToParcel(@NonNull Parcel dest, int flags) { 276 PropertyParcelCreator.writeToParcel(this, dest, flags); 277 } 278 279 /** Builder for {@link PropertyParcel}. */ 280 @ExperimentalAppSearchApi 281 public static final class Builder { 282 private String mPropertyName; 283 private String[] mStringValues; 284 private long[] mLongValues; 285 private double[] mDoubleValues; 286 private boolean[] mBooleanValues; 287 private byte[][] mBytesValues; 288 private GenericDocumentParcel[] mDocumentValues; 289 private EmbeddingVector[] mEmbeddingValues; 290 private AppSearchBlobHandle[] mBlobHandleValues; 291 Builder(@onNull String propertyName)292 public Builder(@NonNull String propertyName) { 293 mPropertyName = Objects.requireNonNull(propertyName); 294 } 295 296 /** Sets String values. */ 297 @CanIgnoreReturnValue setStringValues(String @onNull [] stringValues)298 public @NonNull Builder setStringValues(String @NonNull [] stringValues) { 299 mStringValues = Objects.requireNonNull(stringValues); 300 return this; 301 } 302 303 /** Sets long values. */ 304 @CanIgnoreReturnValue setLongValues(long @NonNull [] longValues)305 public @NonNull Builder setLongValues(long @NonNull [] longValues) { 306 mLongValues = Objects.requireNonNull(longValues); 307 return this; 308 } 309 310 /** Sets double values. */ 311 @CanIgnoreReturnValue setDoubleValues(double @NonNull [] doubleValues)312 public @NonNull Builder setDoubleValues(double @NonNull [] doubleValues) { 313 mDoubleValues = Objects.requireNonNull(doubleValues); 314 return this; 315 } 316 317 /** Sets boolean values. */ 318 @CanIgnoreReturnValue setBooleanValues(boolean @NonNull [] booleanValues)319 public @NonNull Builder setBooleanValues(boolean @NonNull [] booleanValues) { 320 mBooleanValues = Objects.requireNonNull(booleanValues); 321 return this; 322 } 323 324 /** Sets a two dimension byte array. */ 325 @CanIgnoreReturnValue setBytesValues(byte @NonNull [][] bytesValues)326 public @NonNull Builder setBytesValues(byte @NonNull [][] bytesValues) { 327 mBytesValues = Objects.requireNonNull(bytesValues); 328 return this; 329 } 330 331 /** Sets document values. */ 332 @CanIgnoreReturnValue setDocumentValues( GenericDocumentParcel @onNull [] documentValues)333 public @NonNull Builder setDocumentValues( 334 GenericDocumentParcel @NonNull [] documentValues) { 335 mDocumentValues = Objects.requireNonNull(documentValues); 336 return this; 337 } 338 339 /** Sets embedding values. */ 340 @CanIgnoreReturnValue setEmbeddingValues(EmbeddingVector @onNull [] embeddingValues)341 public @NonNull Builder setEmbeddingValues(EmbeddingVector @NonNull [] embeddingValues) { 342 mEmbeddingValues = Objects.requireNonNull(embeddingValues); 343 return this; 344 } 345 346 /** Sets {@link AppSearchBlobHandle} values. */ 347 @CanIgnoreReturnValue setBlobHandleValues( AppSearchBlobHandle @onNull [] blobHandleValues)348 public @NonNull Builder setBlobHandleValues( 349 AppSearchBlobHandle @NonNull [] blobHandleValues) { 350 mBlobHandleValues = Objects.requireNonNull(blobHandleValues); 351 return this; 352 } 353 354 /** Builds a {@link PropertyParcel}. */ build()355 public @NonNull PropertyParcel build() { 356 return new PropertyParcel( 357 mPropertyName, 358 mStringValues, 359 mLongValues, 360 mDoubleValues, 361 mBooleanValues, 362 mBytesValues, 363 mDocumentValues, 364 mEmbeddingValues, 365 mBlobHandleValues); 366 } 367 } 368 } 369