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