blob: 6a3fa6b2f197802b415affa0c45fd82eb4fa86e0 [file] [log] [blame]
Fred Quintanace31b232009-05-04 16:01:15 -07001/*
2 * Copyright (C) 2009 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
17package android.content;
18
Mathew Inwood5c0d3542018-08-14 13:54:31 +010019import android.annotation.UnsupportedAppUsage;
Nicolas Prevotd85fc722014-04-16 19:52:08 +010020import android.content.ContentProvider;
Fred Quintanace31b232009-05-04 16:01:15 -070021import android.database.Cursor;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070022import android.net.Uri;
Fred Quintana89437372009-05-15 15:10:40 -070023import android.os.Parcel;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070024import android.os.Parcelable;
25import android.text.TextUtils;
Ken Shirriff35abad22010-02-19 14:15:32 -080026import android.util.Log;
Fred Quintanace31b232009-05-04 16:01:15 -070027
Jeff Sharkey08b75b12009-08-01 20:13:45 -070028import java.util.ArrayList;
Fred Quintana89437372009-05-15 15:10:40 -070029import java.util.HashMap;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070030import java.util.Map;
Fred Quintanace31b232009-05-04 16:01:15 -070031
Jeff Brown764e95e2015-06-10 17:16:05 -070032/**
33 * Represents a single operation to be performed as part of a batch of operations.
34 *
35 * @see ContentProvider#applyBatch(ArrayList)
36 */
Fred Quintana89437372009-05-15 15:10:40 -070037public class ContentProviderOperation implements Parcelable {
Jeff Sharkeybc254072009-07-28 20:39:35 -070038 /** @hide exposed for unit tests */
Mathew Inwood5c0d3542018-08-14 13:54:31 +010039 @UnsupportedAppUsage
Jeff Sharkeybc254072009-07-28 20:39:35 -070040 public final static int TYPE_INSERT = 1;
41 /** @hide exposed for unit tests */
Mathew Inwood5c0d3542018-08-14 13:54:31 +010042 @UnsupportedAppUsage
Jeff Sharkeybc254072009-07-28 20:39:35 -070043 public final static int TYPE_UPDATE = 2;
44 /** @hide exposed for unit tests */
Mathew Inwood5c0d3542018-08-14 13:54:31 +010045 @UnsupportedAppUsage
Jeff Sharkeybc254072009-07-28 20:39:35 -070046 public final static int TYPE_DELETE = 3;
47 /** @hide exposed for unit tests */
Jeff Sharkey08b75b12009-08-01 20:13:45 -070048 public final static int TYPE_ASSERT = 4;
Fred Quintanace31b232009-05-04 16:01:15 -070049
Mathew Inwood5c0d3542018-08-14 13:54:31 +010050 @UnsupportedAppUsage
Fred Quintanace31b232009-05-04 16:01:15 -070051 private final int mType;
Mathew Inwood5c0d3542018-08-14 13:54:31 +010052 @UnsupportedAppUsage
Fred Quintanace31b232009-05-04 16:01:15 -070053 private final Uri mUri;
Mathew Inwood5c0d3542018-08-14 13:54:31 +010054 @UnsupportedAppUsage
Fred Quintanace31b232009-05-04 16:01:15 -070055 private final String mSelection;
56 private final String[] mSelectionArgs;
57 private final ContentValues mValues;
58 private final Integer mExpectedCount;
59 private final ContentValues mValuesBackReferences;
60 private final Map<Integer, Integer> mSelectionArgsBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -070061 private final boolean mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -070062
Ken Shirriff35abad22010-02-19 14:15:32 -080063 private final static String TAG = "ContentProviderOperation";
64
Fred Quintanace31b232009-05-04 16:01:15 -070065 /**
66 * Creates a {@link ContentProviderOperation} by copying the contents of a
67 * {@link Builder}.
68 */
69 private ContentProviderOperation(Builder builder) {
70 mType = builder.mType;
71 mUri = builder.mUri;
72 mValues = builder.mValues;
73 mSelection = builder.mSelection;
74 mSelectionArgs = builder.mSelectionArgs;
75 mExpectedCount = builder.mExpectedCount;
76 mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
77 mValuesBackReferences = builder.mValuesBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -070078 mYieldAllowed = builder.mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -070079 }
80
Fred Quintana03d94902009-05-22 14:23:31 -070081 private ContentProviderOperation(Parcel source) {
Fred Quintana89437372009-05-15 15:10:40 -070082 mType = source.readInt();
83 mUri = Uri.CREATOR.createFromParcel(source);
84 mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
Fred Quintana89437372009-05-15 15:10:40 -070085 mSelection = source.readInt() != 0 ? source.readString() : null;
86 mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
87 mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
88 mValuesBackReferences = source.readInt() != 0
Fred Quintana89437372009-05-15 15:10:40 -070089 ? ContentValues.CREATOR.createFromParcel(source)
90 : null;
91 mSelectionArgsBackReferences = source.readInt() != 0
92 ? new HashMap<Integer, Integer>()
93 : null;
94 if (mSelectionArgsBackReferences != null) {
95 final int count = source.readInt();
96 for (int i = 0; i < count; i++) {
97 mSelectionArgsBackReferences.put(source.readInt(), source.readInt());
98 }
99 }
Fred Quintana56f67d22009-08-28 14:36:42 -0700100 mYieldAllowed = source.readInt() != 0;
Fred Quintana89437372009-05-15 15:10:40 -0700101 }
102
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100103 /** @hide */
Jeff Sharkeyc4156e02018-09-24 13:23:57 -0600104 public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) {
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100105 mType = cpo.mType;
Jeff Sharkeyc4156e02018-09-24 13:23:57 -0600106 mUri = withUri;
Nicolas Prevotd85fc722014-04-16 19:52:08 +0100107 mValues = cpo.mValues;
108 mSelection = cpo.mSelection;
109 mSelectionArgs = cpo.mSelectionArgs;
110 mExpectedCount = cpo.mExpectedCount;
111 mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
112 mValuesBackReferences = cpo.mValuesBackReferences;
113 mYieldAllowed = cpo.mYieldAllowed;
114 }
115
Fred Quintana89437372009-05-15 15:10:40 -0700116 public void writeToParcel(Parcel dest, int flags) {
117 dest.writeInt(mType);
118 Uri.writeToParcel(dest, mUri);
119 if (mValues != null) {
120 dest.writeInt(1);
121 mValues.writeToParcel(dest, 0);
122 } else {
123 dest.writeInt(0);
124 }
Fred Quintana89437372009-05-15 15:10:40 -0700125 if (mSelection != null) {
126 dest.writeInt(1);
127 dest.writeString(mSelection);
128 } else {
129 dest.writeInt(0);
130 }
131 if (mSelectionArgs != null) {
132 dest.writeInt(1);
133 dest.writeStringArray(mSelectionArgs);
134 } else {
135 dest.writeInt(0);
136 }
137 if (mExpectedCount != null) {
138 dest.writeInt(1);
139 dest.writeInt(mExpectedCount);
140 } else {
141 dest.writeInt(0);
142 }
143 if (mValuesBackReferences != null) {
144 dest.writeInt(1);
145 mValuesBackReferences.writeToParcel(dest, 0);
146 } else {
147 dest.writeInt(0);
148 }
149 if (mSelectionArgsBackReferences != null) {
150 dest.writeInt(1);
151 dest.writeInt(mSelectionArgsBackReferences.size());
152 for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) {
153 dest.writeInt(entry.getKey());
154 dest.writeInt(entry.getValue());
155 }
156 } else {
157 dest.writeInt(0);
158 }
Fred Quintana56f67d22009-08-28 14:36:42 -0700159 dest.writeInt(mYieldAllowed ? 1 : 0);
Fred Quintana89437372009-05-15 15:10:40 -0700160 }
161
Fred Quintanace31b232009-05-04 16:01:15 -0700162 /**
163 * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}.
164 * @param uri The {@link Uri} that is the target of the insert.
165 * @return a {@link Builder}
166 */
167 public static Builder newInsert(Uri uri) {
168 return new Builder(TYPE_INSERT, uri);
169 }
170
171 /**
172 * Create a {@link Builder} suitable for building an update {@link ContentProviderOperation}.
173 * @param uri The {@link Uri} that is the target of the update.
174 * @return a {@link Builder}
175 */
176 public static Builder newUpdate(Uri uri) {
177 return new Builder(TYPE_UPDATE, uri);
178 }
179
180 /**
181 * Create a {@link Builder} suitable for building a delete {@link ContentProviderOperation}.
182 * @param uri The {@link Uri} that is the target of the delete.
183 * @return a {@link Builder}
184 */
185 public static Builder newDelete(Uri uri) {
186 return new Builder(TYPE_DELETE, uri);
187 }
188
189 /**
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700190 * Create a {@link Builder} suitable for building a
191 * {@link ContentProviderOperation} to assert a set of values as provided
192 * through {@link Builder#withValues(ContentValues)}.
Fred Quintanace31b232009-05-04 16:01:15 -0700193 */
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700194 public static Builder newAssertQuery(Uri uri) {
195 return new Builder(TYPE_ASSERT, uri);
Fred Quintanace31b232009-05-04 16:01:15 -0700196 }
197
Jeff Brown764e95e2015-06-10 17:16:05 -0700198 /**
199 * Gets the Uri for the target of the operation.
200 */
Fred Quintana89437372009-05-15 15:10:40 -0700201 public Uri getUri() {
202 return mUri;
203 }
204
Jeff Brown764e95e2015-06-10 17:16:05 -0700205 /**
206 * Returns true if the operation allows yielding the database to other transactions
207 * if the database is contended.
208 *
209 * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
210 */
Fred Quintana56f67d22009-08-28 14:36:42 -0700211 public boolean isYieldAllowed() {
212 return mYieldAllowed;
213 }
214
Jeff Sharkeybc254072009-07-28 20:39:35 -0700215 /** @hide exposed for unit tests */
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100216 @UnsupportedAppUsage
Jeff Sharkeybc254072009-07-28 20:39:35 -0700217 public int getType() {
218 return mType;
219 }
220
Jeff Brown764e95e2015-06-10 17:16:05 -0700221 /**
222 * Returns true if the operation represents an insertion.
223 *
224 * @see #newInsert
225 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800226 public boolean isInsert() {
227 return mType == TYPE_INSERT;
228 }
229
Jeff Brown764e95e2015-06-10 17:16:05 -0700230 /**
231 * Returns true if the operation represents a deletion.
232 *
233 * @see #newDelete
234 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800235 public boolean isDelete() {
236 return mType == TYPE_DELETE;
237 }
238
Jeff Brown764e95e2015-06-10 17:16:05 -0700239 /**
240 * Returns true if the operation represents an update.
241 *
242 * @see #newUpdate
243 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800244 public boolean isUpdate() {
245 return mType == TYPE_UPDATE;
246 }
247
Jeff Brown764e95e2015-06-10 17:16:05 -0700248 /**
249 * Returns true if the operation represents an assert query.
250 *
251 * @see #newAssertQuery
252 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800253 public boolean isAssertQuery() {
254 return mType == TYPE_ASSERT;
255 }
256
Jeff Brown764e95e2015-06-10 17:16:05 -0700257 /**
258 * Returns true if the operation represents an insertion, deletion, or update.
259 *
260 * @see #isInsert
261 * @see #isDelete
262 * @see #isUpdate
263 */
Fred Quintana89437372009-05-15 15:10:40 -0700264 public boolean isWriteOperation() {
265 return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
266 }
267
Jeff Brown764e95e2015-06-10 17:16:05 -0700268 /**
269 * Returns true if the operation represents an assert query.
270 *
271 * @see #isAssertQuery
272 */
Fred Quintana89437372009-05-15 15:10:40 -0700273 public boolean isReadOperation() {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700274 return mType == TYPE_ASSERT;
Fred Quintana89437372009-05-15 15:10:40 -0700275 }
276
Fred Quintanace31b232009-05-04 16:01:15 -0700277 /**
278 * Applies this operation using the given provider. The backRefs array is used to resolve any
279 * back references that were requested using
280 * {@link Builder#withValueBackReferences(ContentValues)} and
Fred Quintana03d94902009-05-22 14:23:31 -0700281 * {@link Builder#withSelectionBackReference}.
Fred Quintanace31b232009-05-04 16:01:15 -0700282 * @param provider the {@link ContentProvider} on which this batch is applied
283 * @param backRefs a {@link ContentProviderResult} array that will be consulted
284 * to resolve any requested back references.
285 * @param numBackRefs the number of valid results on the backRefs array.
286 * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
287 * row if this was an insert otherwise the number of rows affected.
288 * @throws OperationApplicationException thrown if either the insert fails or
289 * if the number of rows affected didn't match the expected count
290 */
291 public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
292 int numBackRefs) throws OperationApplicationException {
293 ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
294 String[] selectionArgs =
295 resolveSelectionArgsBackReferences(backRefs, numBackRefs);
296
297 if (mType == TYPE_INSERT) {
Fred Quintana03d94902009-05-22 14:23:31 -0700298 Uri newUri = provider.insert(mUri, values);
Fred Quintanace31b232009-05-04 16:01:15 -0700299 if (newUri == null) {
300 throw new OperationApplicationException("insert failed");
301 }
302 return new ContentProviderResult(newUri);
303 }
304
305 int numRows;
306 if (mType == TYPE_DELETE) {
307 numRows = provider.delete(mUri, mSelection, selectionArgs);
308 } else if (mType == TYPE_UPDATE) {
Fred Quintana03d94902009-05-22 14:23:31 -0700309 numRows = provider.update(mUri, values, mSelection, selectionArgs);
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700310 } else if (mType == TYPE_ASSERT) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700311 // Assert that all rows match expected values
Fred Quintana5ab78052009-09-15 18:17:07 -0700312 String[] projection = null;
313 if (values != null) {
314 // Build projection map from expected values
315 final ArrayList<String> projectionList = new ArrayList<String>();
316 for (Map.Entry<String, Object> entry : values.valueSet()) {
317 projectionList.add(entry.getKey());
318 }
319 projection = projectionList.toArray(new String[projectionList.size()]);
320 }
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700321 final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
Fred Quintanace31b232009-05-04 16:01:15 -0700322 try {
Fred Quintana5ab78052009-09-15 18:17:07 -0700323 numRows = cursor.getCount();
324 if (projection != null) {
325 while (cursor.moveToNext()) {
326 for (int i = 0; i < projection.length; i++) {
327 final String cursorValue = cursor.getString(i);
328 final String expectedValue = values.getAsString(projection[i]);
329 if (!TextUtils.equals(cursorValue, expectedValue)) {
330 // Throw exception when expected values don't match
Ken Shirriff35abad22010-02-19 14:15:32 -0800331 Log.e(TAG, this.toString());
Fred Quintana5ab78052009-09-15 18:17:07 -0700332 throw new OperationApplicationException("Found value " + cursorValue
333 + " when expected " + expectedValue + " for column "
334 + projection[i]);
335 }
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700336 }
337 }
Fred Quintanace31b232009-05-04 16:01:15 -0700338 }
Fred Quintanace31b232009-05-04 16:01:15 -0700339 } finally {
340 cursor.close();
341 }
342 } else {
Ken Shirriff35abad22010-02-19 14:15:32 -0800343 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700344 throw new IllegalStateException("bad type, " + mType);
345 }
346
347 if (mExpectedCount != null && mExpectedCount != numRows) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800348 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700349 throw new OperationApplicationException("wrong number of rows: " + numRows);
350 }
351
352 return new ContentProviderResult(numRows);
353 }
354
355 /**
356 * The ContentValues back references are represented as a ContentValues object where the
357 * key refers to a column and the value is an index of the back reference whose
358 * valued should be associated with the column.
Joe Onoratod3ad6962010-09-16 13:38:25 -0400359 * <p>
360 * This is intended to be a private method but it is exposed for
361 * unit testing purposes
Fred Quintanace31b232009-05-04 16:01:15 -0700362 * @param backRefs an array of previous results
363 * @param numBackRefs the number of valid previous results in backRefs
364 * @return the ContentValues that should be used in this operation application after
365 * expansion of back references. This can be called if either mValues or mValuesBackReferences
366 * is null
Fred Quintanace31b232009-05-04 16:01:15 -0700367 */
368 public ContentValues resolveValueBackReferences(
369 ContentProviderResult[] backRefs, int numBackRefs) {
370 if (mValuesBackReferences == null) {
371 return mValues;
372 }
373 final ContentValues values;
374 if (mValues == null) {
375 values = new ContentValues();
376 } else {
377 values = new ContentValues(mValues);
378 }
379 for (Map.Entry<String, Object> entry : mValuesBackReferences.valueSet()) {
380 String key = entry.getKey();
381 Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
382 if (backRefIndex == null) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800383 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700384 throw new IllegalArgumentException("values backref " + key + " is not an integer");
385 }
386 values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
387 }
388 return values;
389 }
390
391 /**
392 * The Selection Arguments back references are represented as a Map of Integer->Integer where
393 * the key is an index into the selection argument array (see {@link Builder#withSelection})
394 * and the value is the index of the previous result that should be used for that selection
395 * argument array slot.
Joe Onoratod3ad6962010-09-16 13:38:25 -0400396 * <p>
397 * This is intended to be a private method but it is exposed for
398 * unit testing purposes
Fred Quintanace31b232009-05-04 16:01:15 -0700399 * @param backRefs an array of previous results
400 * @param numBackRefs the number of valid previous results in backRefs
401 * @return the ContentValues that should be used in this operation application after
402 * expansion of back references. This can be called if either mValues or mValuesBackReferences
403 * is null
Fred Quintanace31b232009-05-04 16:01:15 -0700404 */
405 public String[] resolveSelectionArgsBackReferences(
406 ContentProviderResult[] backRefs, int numBackRefs) {
407 if (mSelectionArgsBackReferences == null) {
408 return mSelectionArgs;
409 }
410 String[] newArgs = new String[mSelectionArgs.length];
411 System.arraycopy(mSelectionArgs, 0, newArgs, 0, mSelectionArgs.length);
412 for (Map.Entry<Integer, Integer> selectionArgBackRef
413 : mSelectionArgsBackReferences.entrySet()) {
414 final Integer selectionArgIndex = selectionArgBackRef.getKey();
415 final int backRefIndex = selectionArgBackRef.getValue();
Fred Quintana8851e162009-08-05 21:06:45 -0700416 newArgs[selectionArgIndex] =
417 String.valueOf(backRefToValue(backRefs, numBackRefs, backRefIndex));
Fred Quintanace31b232009-05-04 16:01:15 -0700418 }
419 return newArgs;
420 }
421
Ken Shirriff35abad22010-02-19 14:15:32 -0800422 @Override
423 public String toString() {
424 return "mType: " + mType + ", mUri: " + mUri +
425 ", mSelection: " + mSelection +
426 ", mExpectedCount: " + mExpectedCount +
427 ", mYieldAllowed: " + mYieldAllowed +
428 ", mValues: " + mValues +
429 ", mValuesBackReferences: " + mValuesBackReferences +
430 ", mSelectionArgsBackReferences: " + mSelectionArgsBackReferences;
431 }
432
Fred Quintanace31b232009-05-04 16:01:15 -0700433 /**
434 * Return the string representation of the requested back reference.
435 * @param backRefs an array of results
436 * @param numBackRefs the number of items in the backRefs array that are valid
437 * @param backRefIndex which backRef to be used
438 * @throws ArrayIndexOutOfBoundsException thrown if the backRefIndex is larger than
439 * the numBackRefs
440 * @return the string representation of the requested back reference.
441 */
Ken Shirriff35abad22010-02-19 14:15:32 -0800442 private long backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
Fred Quintanace31b232009-05-04 16:01:15 -0700443 Integer backRefIndex) {
Fred Quintana03d94902009-05-22 14:23:31 -0700444 if (backRefIndex >= numBackRefs) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800445 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700446 throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
447 + " but there are only " + numBackRefs + " back refs");
448 }
449 ContentProviderResult backRef = backRefs[backRefIndex];
Fred Quintana8851e162009-08-05 21:06:45 -0700450 long backRefValue;
Fred Quintanace31b232009-05-04 16:01:15 -0700451 if (backRef.uri != null) {
Fred Quintana8851e162009-08-05 21:06:45 -0700452 backRefValue = ContentUris.parseId(backRef.uri);
Fred Quintanace31b232009-05-04 16:01:15 -0700453 } else {
Fred Quintana8851e162009-08-05 21:06:45 -0700454 backRefValue = backRef.count;
Fred Quintanace31b232009-05-04 16:01:15 -0700455 }
456 return backRefValue;
457 }
458
Fred Quintana89437372009-05-15 15:10:40 -0700459 public int describeContents() {
460 return 0;
461 }
462
463 public static final Creator<ContentProviderOperation> CREATOR =
464 new Creator<ContentProviderOperation>() {
465 public ContentProviderOperation createFromParcel(Parcel source) {
Fred Quintana03d94902009-05-22 14:23:31 -0700466 return new ContentProviderOperation(source);
Fred Quintana89437372009-05-15 15:10:40 -0700467 }
468
469 public ContentProviderOperation[] newArray(int size) {
470 return new ContentProviderOperation[size];
471 }
472 };
473
Fred Quintanace31b232009-05-04 16:01:15 -0700474 /**
475 * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
476 * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
477 * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
478 * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700479 * {@link ContentProviderOperation#newAssertQuery(Uri)}. The withXXX methods
Fred Quintanace31b232009-05-04 16:01:15 -0700480 * can then be used to add parameters to the builder. See the specific methods to find for
481 * which {@link Builder} type each is allowed. Call {@link #build} to create the
482 * {@link ContentProviderOperation} once all the parameters have been supplied.
483 */
484 public static class Builder {
485 private final int mType;
486 private final Uri mUri;
487 private String mSelection;
488 private String[] mSelectionArgs;
489 private ContentValues mValues;
490 private Integer mExpectedCount;
491 private ContentValues mValuesBackReferences;
492 private Map<Integer, Integer> mSelectionArgsBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -0700493 private boolean mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -0700494
495 /** Create a {@link Builder} of a given type. The uri must not be null. */
496 private Builder(int type, Uri uri) {
497 if (uri == null) {
498 throw new IllegalArgumentException("uri must not be null");
499 }
500 mType = type;
501 mUri = uri;
502 }
503
Fred Quintana89437372009-05-15 15:10:40 -0700504 /** Create a ContentProviderOperation from this {@link Builder}. */
Fred Quintanace31b232009-05-04 16:01:15 -0700505 public ContentProviderOperation build() {
Fred Quintana5ab78052009-09-15 18:17:07 -0700506 if (mType == TYPE_UPDATE) {
Mike Tsaoc74ee2f2017-03-24 14:56:34 -0700507 if ((mValues == null || mValues.isEmpty())
508 && (mValuesBackReferences == null || mValuesBackReferences.isEmpty())) {
Fred Quintanac933fb62009-06-11 12:14:40 -0700509 throw new IllegalArgumentException("Empty values");
510 }
511 }
Fred Quintana5ab78052009-09-15 18:17:07 -0700512 if (mType == TYPE_ASSERT) {
Mike Tsaoc74ee2f2017-03-24 14:56:34 -0700513 if ((mValues == null || mValues.isEmpty())
514 && (mValuesBackReferences == null || mValuesBackReferences.isEmpty())
Fred Quintana5ab78052009-09-15 18:17:07 -0700515 && (mExpectedCount == null)) {
516 throw new IllegalArgumentException("Empty values");
517 }
518 }
Fred Quintanace31b232009-05-04 16:01:15 -0700519 return new ContentProviderOperation(this);
520 }
521
522 /**
523 * Add a {@link ContentValues} of back references. The key is the name of the column
524 * and the value is an integer that is the index of the previous result whose
525 * value should be used for the column. The value is added as a {@link String}.
526 * A column value from the back references takes precedence over a value specified in
527 * {@link #withValues}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700528 * This can only be used with builders of type insert, update, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700529 * @return this builder, to allow for chaining.
530 */
531 public Builder withValueBackReferences(ContentValues backReferences) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700532 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700533 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700534 "only inserts, updates, and asserts can have value back-references");
Fred Quintanace31b232009-05-04 16:01:15 -0700535 }
536 mValuesBackReferences = backReferences;
537 return this;
538 }
539
540 /**
Fred Quintana03d94902009-05-22 14:23:31 -0700541 * Add a ContentValues back reference.
542 * A column value from the back references takes precedence over a value specified in
543 * {@link #withValues}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700544 * This can only be used with builders of type insert, update, or assert.
Fred Quintana03d94902009-05-22 14:23:31 -0700545 * @return this builder, to allow for chaining.
546 */
547 public Builder withValueBackReference(String key, int previousResult) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700548 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintana03d94902009-05-22 14:23:31 -0700549 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700550 "only inserts, updates, and asserts can have value back-references");
Fred Quintana03d94902009-05-22 14:23:31 -0700551 }
552 if (mValuesBackReferences == null) {
553 mValuesBackReferences = new ContentValues();
554 }
555 mValuesBackReferences.put(key, previousResult);
556 return this;
557 }
558
559 /**
560 * Add a back references as a selection arg. Any value at that index of the selection arg
Fred Quintana89437372009-05-15 15:10:40 -0700561 * that was specified by {@link #withSelection} will be overwritten.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700562 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700563 * @return this builder, to allow for chaining.
564 */
Fred Quintana03d94902009-05-22 14:23:31 -0700565 public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700566 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
567 throw new IllegalArgumentException("only updates, deletes, and asserts "
568 + "can have selection back-references");
Fred Quintanace31b232009-05-04 16:01:15 -0700569 }
Fred Quintana03d94902009-05-22 14:23:31 -0700570 if (mSelectionArgsBackReferences == null) {
571 mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
572 }
573 mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
Fred Quintanace31b232009-05-04 16:01:15 -0700574 return this;
575 }
576
577 /**
578 * The ContentValues to use. This may be null. These values may be overwritten by
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700579 * the corresponding value specified by {@link #withValueBackReference} or by
580 * future calls to {@link #withValues} or {@link #withValue}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700581 * This can only be used with builders of type insert, update, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700582 * @return this builder, to allow for chaining.
583 */
584 public Builder withValues(ContentValues values) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700585 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
586 throw new IllegalArgumentException(
587 "only inserts, updates, and asserts can have values");
Fred Quintanace31b232009-05-04 16:01:15 -0700588 }
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700589 if (mValues == null) {
590 mValues = new ContentValues();
591 }
592 mValues.putAll(values);
Fred Quintanace31b232009-05-04 16:01:15 -0700593 return this;
594 }
595
596 /**
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700597 * A value to insert or update. This value may be overwritten by
598 * the corresponding value specified by {@link #withValueBackReference}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700599 * This can only be used with builders of type insert, update, or assert.
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700600 * @param key the name of this value
601 * @param value the value itself. the type must be acceptable for insertion by
602 * {@link ContentValues#put}
603 * @return this builder, to allow for chaining.
604 */
605 public Builder withValue(String key, Object value) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700606 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700607 throw new IllegalArgumentException("only inserts and updates can have values");
608 }
609 if (mValues == null) {
610 mValues = new ContentValues();
611 }
612 if (value == null) {
613 mValues.putNull(key);
614 } else if (value instanceof String) {
615 mValues.put(key, (String) value);
616 } else if (value instanceof Byte) {
617 mValues.put(key, (Byte) value);
618 } else if (value instanceof Short) {
619 mValues.put(key, (Short) value);
620 } else if (value instanceof Integer) {
621 mValues.put(key, (Integer) value);
622 } else if (value instanceof Long) {
623 mValues.put(key, (Long) value);
624 } else if (value instanceof Float) {
625 mValues.put(key, (Float) value);
626 } else if (value instanceof Double) {
627 mValues.put(key, (Double) value);
628 } else if (value instanceof Boolean) {
629 mValues.put(key, (Boolean) value);
630 } else if (value instanceof byte[]) {
631 mValues.put(key, (byte[]) value);
632 } else {
633 throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
634 }
635 return this;
636 }
Jeff Sharkeybc254072009-07-28 20:39:35 -0700637
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700638 /**
Fred Quintanace31b232009-05-04 16:01:15 -0700639 * The selection and arguments to use. An occurrence of '?' in the selection will be
kopriva219f7dc2018-10-09 13:42:28 -0700640 * replaced with the corresponding occurrence of the selection argument. Any of the
Fred Quintanace31b232009-05-04 16:01:15 -0700641 * selection arguments may be overwritten by a selection argument back reference as
Fred Quintana03d94902009-05-22 14:23:31 -0700642 * specified by {@link #withSelectionBackReference}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700643 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700644 * @return this builder, to allow for chaining.
645 */
646 public Builder withSelection(String selection, String[] selectionArgs) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700647 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700648 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700649 "only updates, deletes, and asserts can have selections");
Fred Quintanace31b232009-05-04 16:01:15 -0700650 }
651 mSelection = selection;
Jeff Sharkey824838d2009-10-29 16:39:36 -0700652 if (selectionArgs == null) {
653 mSelectionArgs = null;
654 } else {
655 mSelectionArgs = new String[selectionArgs.length];
656 System.arraycopy(selectionArgs, 0, mSelectionArgs, 0, selectionArgs.length);
657 }
Fred Quintanace31b232009-05-04 16:01:15 -0700658 return this;
659 }
660
661 /**
Jeff Brown764e95e2015-06-10 17:16:05 -0700662 * If set then if the number of rows affected by this operation does not match
Fred Quintanace31b232009-05-04 16:01:15 -0700663 * this count {@link OperationApplicationException} will be throw.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700664 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700665 * @return this builder, to allow for chaining.
666 */
667 public Builder withExpectedCount(int count) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700668 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700669 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700670 "only updates, deletes, and asserts can have expected counts");
Fred Quintanace31b232009-05-04 16:01:15 -0700671 }
672 mExpectedCount = count;
673 return this;
674 }
Fred Quintana56f67d22009-08-28 14:36:42 -0700675
Jeff Brown764e95e2015-06-10 17:16:05 -0700676 /**
677 * If set to true then the operation allows yielding the database to other transactions
678 * if the database is contended.
679 * @return this builder, to allow for chaining.
680 * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
681 */
Fred Quintana56f67d22009-08-28 14:36:42 -0700682 public Builder withYieldAllowed(boolean yieldAllowed) {
683 mYieldAllowed = yieldAllowed;
684 return this;
685 }
Fred Quintanace31b232009-05-04 16:01:15 -0700686 }
687}