blob: fd1e24a48da507eced0ca88e2b80a3852e17f3e7 [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
Nicolas Prevotd85fc722014-04-16 19:52:08 +010019import android.content.ContentProvider;
Fred Quintanace31b232009-05-04 16:01:15 -070020import android.database.Cursor;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070021import android.net.Uri;
Fred Quintana89437372009-05-15 15:10:40 -070022import android.os.Parcel;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070023import android.os.Parcelable;
24import android.text.TextUtils;
Ken Shirriff35abad22010-02-19 14:15:32 -080025import android.util.Log;
Fred Quintanace31b232009-05-04 16:01:15 -070026
Jeff Sharkey08b75b12009-08-01 20:13:45 -070027import java.util.ArrayList;
Fred Quintana89437372009-05-15 15:10:40 -070028import java.util.HashMap;
Jeff Sharkey08b75b12009-08-01 20:13:45 -070029import java.util.Map;
Fred Quintanace31b232009-05-04 16:01:15 -070030
Jeff Brown764e95e2015-06-10 17:16:05 -070031/**
32 * Represents a single operation to be performed as part of a batch of operations.
33 *
34 * @see ContentProvider#applyBatch(ArrayList)
35 */
Fred Quintana89437372009-05-15 15:10:40 -070036public class ContentProviderOperation implements Parcelable {
Jeff Sharkeybc254072009-07-28 20:39:35 -070037 /** @hide exposed for unit tests */
38 public final static int TYPE_INSERT = 1;
39 /** @hide exposed for unit tests */
40 public final static int TYPE_UPDATE = 2;
41 /** @hide exposed for unit tests */
42 public final static int TYPE_DELETE = 3;
43 /** @hide exposed for unit tests */
Jeff Sharkey08b75b12009-08-01 20:13:45 -070044 public final static int TYPE_ASSERT = 4;
Fred Quintanace31b232009-05-04 16:01:15 -070045
46 private final int mType;
47 private final Uri mUri;
48 private final String mSelection;
49 private final String[] mSelectionArgs;
50 private final ContentValues mValues;
51 private final Integer mExpectedCount;
52 private final ContentValues mValuesBackReferences;
53 private final Map<Integer, Integer> mSelectionArgsBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -070054 private final boolean mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -070055
Ken Shirriff35abad22010-02-19 14:15:32 -080056 private final static String TAG = "ContentProviderOperation";
57
Fred Quintanace31b232009-05-04 16:01:15 -070058 /**
59 * Creates a {@link ContentProviderOperation} by copying the contents of a
60 * {@link Builder}.
61 */
62 private ContentProviderOperation(Builder builder) {
63 mType = builder.mType;
64 mUri = builder.mUri;
65 mValues = builder.mValues;
66 mSelection = builder.mSelection;
67 mSelectionArgs = builder.mSelectionArgs;
68 mExpectedCount = builder.mExpectedCount;
69 mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
70 mValuesBackReferences = builder.mValuesBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -070071 mYieldAllowed = builder.mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -070072 }
73
Fred Quintana03d94902009-05-22 14:23:31 -070074 private ContentProviderOperation(Parcel source) {
Fred Quintana89437372009-05-15 15:10:40 -070075 mType = source.readInt();
76 mUri = Uri.CREATOR.createFromParcel(source);
77 mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
Fred Quintana89437372009-05-15 15:10:40 -070078 mSelection = source.readInt() != 0 ? source.readString() : null;
79 mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
80 mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
81 mValuesBackReferences = source.readInt() != 0
Fred Quintana89437372009-05-15 15:10:40 -070082 ? ContentValues.CREATOR.createFromParcel(source)
83 : null;
84 mSelectionArgsBackReferences = source.readInt() != 0
85 ? new HashMap<Integer, Integer>()
86 : null;
87 if (mSelectionArgsBackReferences != null) {
88 final int count = source.readInt();
89 for (int i = 0; i < count; i++) {
90 mSelectionArgsBackReferences.put(source.readInt(), source.readInt());
91 }
92 }
Fred Quintana56f67d22009-08-28 14:36:42 -070093 mYieldAllowed = source.readInt() != 0;
Fred Quintana89437372009-05-15 15:10:40 -070094 }
95
Nicolas Prevotd85fc722014-04-16 19:52:08 +010096 /** @hide */
97 public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
98 mType = cpo.mType;
99 if (removeUserIdFromUri) {
100 mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
101 } else {
102 mUri = cpo.mUri;
103 }
104 mValues = cpo.mValues;
105 mSelection = cpo.mSelection;
106 mSelectionArgs = cpo.mSelectionArgs;
107 mExpectedCount = cpo.mExpectedCount;
108 mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
109 mValuesBackReferences = cpo.mValuesBackReferences;
110 mYieldAllowed = cpo.mYieldAllowed;
111 }
112
113 /** @hide */
114 public ContentProviderOperation getWithoutUserIdInUri() {
115 if (ContentProvider.uriHasUserId(mUri)) {
116 return new ContentProviderOperation(this, true);
117 }
118 return this;
119 }
120
Fred Quintana89437372009-05-15 15:10:40 -0700121 public void writeToParcel(Parcel dest, int flags) {
122 dest.writeInt(mType);
123 Uri.writeToParcel(dest, mUri);
124 if (mValues != null) {
125 dest.writeInt(1);
126 mValues.writeToParcel(dest, 0);
127 } else {
128 dest.writeInt(0);
129 }
Fred Quintana89437372009-05-15 15:10:40 -0700130 if (mSelection != null) {
131 dest.writeInt(1);
132 dest.writeString(mSelection);
133 } else {
134 dest.writeInt(0);
135 }
136 if (mSelectionArgs != null) {
137 dest.writeInt(1);
138 dest.writeStringArray(mSelectionArgs);
139 } else {
140 dest.writeInt(0);
141 }
142 if (mExpectedCount != null) {
143 dest.writeInt(1);
144 dest.writeInt(mExpectedCount);
145 } else {
146 dest.writeInt(0);
147 }
148 if (mValuesBackReferences != null) {
149 dest.writeInt(1);
150 mValuesBackReferences.writeToParcel(dest, 0);
151 } else {
152 dest.writeInt(0);
153 }
154 if (mSelectionArgsBackReferences != null) {
155 dest.writeInt(1);
156 dest.writeInt(mSelectionArgsBackReferences.size());
157 for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) {
158 dest.writeInt(entry.getKey());
159 dest.writeInt(entry.getValue());
160 }
161 } else {
162 dest.writeInt(0);
163 }
Fred Quintana56f67d22009-08-28 14:36:42 -0700164 dest.writeInt(mYieldAllowed ? 1 : 0);
Fred Quintana89437372009-05-15 15:10:40 -0700165 }
166
Fred Quintanace31b232009-05-04 16:01:15 -0700167 /**
168 * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}.
169 * @param uri The {@link Uri} that is the target of the insert.
170 * @return a {@link Builder}
171 */
172 public static Builder newInsert(Uri uri) {
173 return new Builder(TYPE_INSERT, uri);
174 }
175
176 /**
177 * Create a {@link Builder} suitable for building an update {@link ContentProviderOperation}.
178 * @param uri The {@link Uri} that is the target of the update.
179 * @return a {@link Builder}
180 */
181 public static Builder newUpdate(Uri uri) {
182 return new Builder(TYPE_UPDATE, uri);
183 }
184
185 /**
186 * Create a {@link Builder} suitable for building a delete {@link ContentProviderOperation}.
187 * @param uri The {@link Uri} that is the target of the delete.
188 * @return a {@link Builder}
189 */
190 public static Builder newDelete(Uri uri) {
191 return new Builder(TYPE_DELETE, uri);
192 }
193
194 /**
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700195 * Create a {@link Builder} suitable for building a
196 * {@link ContentProviderOperation} to assert a set of values as provided
197 * through {@link Builder#withValues(ContentValues)}.
Fred Quintanace31b232009-05-04 16:01:15 -0700198 */
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700199 public static Builder newAssertQuery(Uri uri) {
200 return new Builder(TYPE_ASSERT, uri);
Fred Quintanace31b232009-05-04 16:01:15 -0700201 }
202
Jeff Brown764e95e2015-06-10 17:16:05 -0700203 /**
204 * Gets the Uri for the target of the operation.
205 */
Fred Quintana89437372009-05-15 15:10:40 -0700206 public Uri getUri() {
207 return mUri;
208 }
209
Jeff Brown764e95e2015-06-10 17:16:05 -0700210 /**
211 * Returns true if the operation allows yielding the database to other transactions
212 * if the database is contended.
213 *
214 * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
215 */
Fred Quintana56f67d22009-08-28 14:36:42 -0700216 public boolean isYieldAllowed() {
217 return mYieldAllowed;
218 }
219
Jeff Sharkeybc254072009-07-28 20:39:35 -0700220 /** @hide exposed for unit tests */
221 public int getType() {
222 return mType;
223 }
224
Jeff Brown764e95e2015-06-10 17:16:05 -0700225 /**
226 * Returns true if the operation represents an insertion.
227 *
228 * @see #newInsert
229 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800230 public boolean isInsert() {
231 return mType == TYPE_INSERT;
232 }
233
Jeff Brown764e95e2015-06-10 17:16:05 -0700234 /**
235 * Returns true if the operation represents a deletion.
236 *
237 * @see #newDelete
238 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800239 public boolean isDelete() {
240 return mType == TYPE_DELETE;
241 }
242
Jeff Brown764e95e2015-06-10 17:16:05 -0700243 /**
244 * Returns true if the operation represents an update.
245 *
246 * @see #newUpdate
247 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800248 public boolean isUpdate() {
249 return mType == TYPE_UPDATE;
250 }
251
Jeff Brown764e95e2015-06-10 17:16:05 -0700252 /**
253 * Returns true if the operation represents an assert query.
254 *
255 * @see #newAssertQuery
256 */
Brian Attwell1cf74052015-01-22 12:52:00 -0800257 public boolean isAssertQuery() {
258 return mType == TYPE_ASSERT;
259 }
260
Jeff Brown764e95e2015-06-10 17:16:05 -0700261 /**
262 * Returns true if the operation represents an insertion, deletion, or update.
263 *
264 * @see #isInsert
265 * @see #isDelete
266 * @see #isUpdate
267 */
Fred Quintana89437372009-05-15 15:10:40 -0700268 public boolean isWriteOperation() {
269 return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
270 }
271
Jeff Brown764e95e2015-06-10 17:16:05 -0700272 /**
273 * Returns true if the operation represents an assert query.
274 *
275 * @see #isAssertQuery
276 */
Fred Quintana89437372009-05-15 15:10:40 -0700277 public boolean isReadOperation() {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700278 return mType == TYPE_ASSERT;
Fred Quintana89437372009-05-15 15:10:40 -0700279 }
280
Fred Quintanace31b232009-05-04 16:01:15 -0700281 /**
282 * Applies this operation using the given provider. The backRefs array is used to resolve any
283 * back references that were requested using
284 * {@link Builder#withValueBackReferences(ContentValues)} and
Fred Quintana03d94902009-05-22 14:23:31 -0700285 * {@link Builder#withSelectionBackReference}.
Fred Quintanace31b232009-05-04 16:01:15 -0700286 * @param provider the {@link ContentProvider} on which this batch is applied
287 * @param backRefs a {@link ContentProviderResult} array that will be consulted
288 * to resolve any requested back references.
289 * @param numBackRefs the number of valid results on the backRefs array.
290 * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
291 * row if this was an insert otherwise the number of rows affected.
292 * @throws OperationApplicationException thrown if either the insert fails or
293 * if the number of rows affected didn't match the expected count
294 */
295 public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
296 int numBackRefs) throws OperationApplicationException {
297 ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
298 String[] selectionArgs =
299 resolveSelectionArgsBackReferences(backRefs, numBackRefs);
300
301 if (mType == TYPE_INSERT) {
Fred Quintana03d94902009-05-22 14:23:31 -0700302 Uri newUri = provider.insert(mUri, values);
Fred Quintanace31b232009-05-04 16:01:15 -0700303 if (newUri == null) {
304 throw new OperationApplicationException("insert failed");
305 }
306 return new ContentProviderResult(newUri);
307 }
308
309 int numRows;
310 if (mType == TYPE_DELETE) {
311 numRows = provider.delete(mUri, mSelection, selectionArgs);
312 } else if (mType == TYPE_UPDATE) {
Fred Quintana03d94902009-05-22 14:23:31 -0700313 numRows = provider.update(mUri, values, mSelection, selectionArgs);
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700314 } else if (mType == TYPE_ASSERT) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700315 // Assert that all rows match expected values
Fred Quintana5ab78052009-09-15 18:17:07 -0700316 String[] projection = null;
317 if (values != null) {
318 // Build projection map from expected values
319 final ArrayList<String> projectionList = new ArrayList<String>();
320 for (Map.Entry<String, Object> entry : values.valueSet()) {
321 projectionList.add(entry.getKey());
322 }
323 projection = projectionList.toArray(new String[projectionList.size()]);
324 }
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700325 final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
Fred Quintanace31b232009-05-04 16:01:15 -0700326 try {
Fred Quintana5ab78052009-09-15 18:17:07 -0700327 numRows = cursor.getCount();
328 if (projection != null) {
329 while (cursor.moveToNext()) {
330 for (int i = 0; i < projection.length; i++) {
331 final String cursorValue = cursor.getString(i);
332 final String expectedValue = values.getAsString(projection[i]);
333 if (!TextUtils.equals(cursorValue, expectedValue)) {
334 // Throw exception when expected values don't match
Ken Shirriff35abad22010-02-19 14:15:32 -0800335 Log.e(TAG, this.toString());
Fred Quintana5ab78052009-09-15 18:17:07 -0700336 throw new OperationApplicationException("Found value " + cursorValue
337 + " when expected " + expectedValue + " for column "
338 + projection[i]);
339 }
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700340 }
341 }
Fred Quintanace31b232009-05-04 16:01:15 -0700342 }
Fred Quintanace31b232009-05-04 16:01:15 -0700343 } finally {
344 cursor.close();
345 }
346 } else {
Ken Shirriff35abad22010-02-19 14:15:32 -0800347 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700348 throw new IllegalStateException("bad type, " + mType);
349 }
350
351 if (mExpectedCount != null && mExpectedCount != numRows) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800352 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700353 throw new OperationApplicationException("wrong number of rows: " + numRows);
354 }
355
356 return new ContentProviderResult(numRows);
357 }
358
359 /**
360 * The ContentValues back references are represented as a ContentValues object where the
361 * key refers to a column and the value is an index of the back reference whose
362 * valued should be associated with the column.
Joe Onoratod3ad6962010-09-16 13:38:25 -0400363 * <p>
364 * This is intended to be a private method but it is exposed for
365 * unit testing purposes
Fred Quintanace31b232009-05-04 16:01:15 -0700366 * @param backRefs an array of previous results
367 * @param numBackRefs the number of valid previous results in backRefs
368 * @return the ContentValues that should be used in this operation application after
369 * expansion of back references. This can be called if either mValues or mValuesBackReferences
370 * is null
Fred Quintanace31b232009-05-04 16:01:15 -0700371 */
372 public ContentValues resolveValueBackReferences(
373 ContentProviderResult[] backRefs, int numBackRefs) {
374 if (mValuesBackReferences == null) {
375 return mValues;
376 }
377 final ContentValues values;
378 if (mValues == null) {
379 values = new ContentValues();
380 } else {
381 values = new ContentValues(mValues);
382 }
383 for (Map.Entry<String, Object> entry : mValuesBackReferences.valueSet()) {
384 String key = entry.getKey();
385 Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
386 if (backRefIndex == null) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800387 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700388 throw new IllegalArgumentException("values backref " + key + " is not an integer");
389 }
390 values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
391 }
392 return values;
393 }
394
395 /**
396 * The Selection Arguments back references are represented as a Map of Integer->Integer where
397 * the key is an index into the selection argument array (see {@link Builder#withSelection})
398 * and the value is the index of the previous result that should be used for that selection
399 * argument array slot.
Joe Onoratod3ad6962010-09-16 13:38:25 -0400400 * <p>
401 * This is intended to be a private method but it is exposed for
402 * unit testing purposes
Fred Quintanace31b232009-05-04 16:01:15 -0700403 * @param backRefs an array of previous results
404 * @param numBackRefs the number of valid previous results in backRefs
405 * @return the ContentValues that should be used in this operation application after
406 * expansion of back references. This can be called if either mValues or mValuesBackReferences
407 * is null
Fred Quintanace31b232009-05-04 16:01:15 -0700408 */
409 public String[] resolveSelectionArgsBackReferences(
410 ContentProviderResult[] backRefs, int numBackRefs) {
411 if (mSelectionArgsBackReferences == null) {
412 return mSelectionArgs;
413 }
414 String[] newArgs = new String[mSelectionArgs.length];
415 System.arraycopy(mSelectionArgs, 0, newArgs, 0, mSelectionArgs.length);
416 for (Map.Entry<Integer, Integer> selectionArgBackRef
417 : mSelectionArgsBackReferences.entrySet()) {
418 final Integer selectionArgIndex = selectionArgBackRef.getKey();
419 final int backRefIndex = selectionArgBackRef.getValue();
Fred Quintana8851e162009-08-05 21:06:45 -0700420 newArgs[selectionArgIndex] =
421 String.valueOf(backRefToValue(backRefs, numBackRefs, backRefIndex));
Fred Quintanace31b232009-05-04 16:01:15 -0700422 }
423 return newArgs;
424 }
425
Ken Shirriff35abad22010-02-19 14:15:32 -0800426 @Override
427 public String toString() {
428 return "mType: " + mType + ", mUri: " + mUri +
429 ", mSelection: " + mSelection +
430 ", mExpectedCount: " + mExpectedCount +
431 ", mYieldAllowed: " + mYieldAllowed +
432 ", mValues: " + mValues +
433 ", mValuesBackReferences: " + mValuesBackReferences +
434 ", mSelectionArgsBackReferences: " + mSelectionArgsBackReferences;
435 }
436
Fred Quintanace31b232009-05-04 16:01:15 -0700437 /**
438 * Return the string representation of the requested back reference.
439 * @param backRefs an array of results
440 * @param numBackRefs the number of items in the backRefs array that are valid
441 * @param backRefIndex which backRef to be used
442 * @throws ArrayIndexOutOfBoundsException thrown if the backRefIndex is larger than
443 * the numBackRefs
444 * @return the string representation of the requested back reference.
445 */
Ken Shirriff35abad22010-02-19 14:15:32 -0800446 private long backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
Fred Quintanace31b232009-05-04 16:01:15 -0700447 Integer backRefIndex) {
Fred Quintana03d94902009-05-22 14:23:31 -0700448 if (backRefIndex >= numBackRefs) {
Ken Shirriff35abad22010-02-19 14:15:32 -0800449 Log.e(TAG, this.toString());
Fred Quintanace31b232009-05-04 16:01:15 -0700450 throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
451 + " but there are only " + numBackRefs + " back refs");
452 }
453 ContentProviderResult backRef = backRefs[backRefIndex];
Fred Quintana8851e162009-08-05 21:06:45 -0700454 long backRefValue;
Fred Quintanace31b232009-05-04 16:01:15 -0700455 if (backRef.uri != null) {
Fred Quintana8851e162009-08-05 21:06:45 -0700456 backRefValue = ContentUris.parseId(backRef.uri);
Fred Quintanace31b232009-05-04 16:01:15 -0700457 } else {
Fred Quintana8851e162009-08-05 21:06:45 -0700458 backRefValue = backRef.count;
Fred Quintanace31b232009-05-04 16:01:15 -0700459 }
460 return backRefValue;
461 }
462
Fred Quintana89437372009-05-15 15:10:40 -0700463 public int describeContents() {
464 return 0;
465 }
466
467 public static final Creator<ContentProviderOperation> CREATOR =
468 new Creator<ContentProviderOperation>() {
469 public ContentProviderOperation createFromParcel(Parcel source) {
Fred Quintana03d94902009-05-22 14:23:31 -0700470 return new ContentProviderOperation(source);
Fred Quintana89437372009-05-15 15:10:40 -0700471 }
472
473 public ContentProviderOperation[] newArray(int size) {
474 return new ContentProviderOperation[size];
475 }
476 };
477
Fred Quintanace31b232009-05-04 16:01:15 -0700478 /**
479 * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
480 * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
481 * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
482 * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700483 * {@link ContentProviderOperation#newAssertQuery(Uri)}. The withXXX methods
Fred Quintanace31b232009-05-04 16:01:15 -0700484 * can then be used to add parameters to the builder. See the specific methods to find for
485 * which {@link Builder} type each is allowed. Call {@link #build} to create the
486 * {@link ContentProviderOperation} once all the parameters have been supplied.
487 */
488 public static class Builder {
489 private final int mType;
490 private final Uri mUri;
491 private String mSelection;
492 private String[] mSelectionArgs;
493 private ContentValues mValues;
494 private Integer mExpectedCount;
495 private ContentValues mValuesBackReferences;
496 private Map<Integer, Integer> mSelectionArgsBackReferences;
Fred Quintana56f67d22009-08-28 14:36:42 -0700497 private boolean mYieldAllowed;
Fred Quintanace31b232009-05-04 16:01:15 -0700498
499 /** Create a {@link Builder} of a given type. The uri must not be null. */
500 private Builder(int type, Uri uri) {
501 if (uri == null) {
502 throw new IllegalArgumentException("uri must not be null");
503 }
504 mType = type;
505 mUri = uri;
506 }
507
Fred Quintana89437372009-05-15 15:10:40 -0700508 /** Create a ContentProviderOperation from this {@link Builder}. */
Fred Quintanace31b232009-05-04 16:01:15 -0700509 public ContentProviderOperation build() {
Fred Quintana5ab78052009-09-15 18:17:07 -0700510 if (mType == TYPE_UPDATE) {
Fred Quintanac933fb62009-06-11 12:14:40 -0700511 if ((mValues == null || mValues.size() == 0)
512 && (mValuesBackReferences == null || mValuesBackReferences.size() == 0)) {
513 throw new IllegalArgumentException("Empty values");
514 }
515 }
Fred Quintana5ab78052009-09-15 18:17:07 -0700516 if (mType == TYPE_ASSERT) {
517 if ((mValues == null || mValues.size() == 0)
518 && (mValuesBackReferences == null || mValuesBackReferences.size() == 0)
519 && (mExpectedCount == null)) {
520 throw new IllegalArgumentException("Empty values");
521 }
522 }
Fred Quintanace31b232009-05-04 16:01:15 -0700523 return new ContentProviderOperation(this);
524 }
525
526 /**
527 * Add a {@link ContentValues} of back references. The key is the name of the column
528 * and the value is an integer that is the index of the previous result whose
529 * value should be used for the column. The value is added as a {@link String}.
530 * A column value from the back references takes precedence over a value specified in
531 * {@link #withValues}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700532 * This can only be used with builders of type insert, update, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700533 * @return this builder, to allow for chaining.
534 */
535 public Builder withValueBackReferences(ContentValues backReferences) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700536 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700537 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700538 "only inserts, updates, and asserts can have value back-references");
Fred Quintanace31b232009-05-04 16:01:15 -0700539 }
540 mValuesBackReferences = backReferences;
541 return this;
542 }
543
544 /**
Fred Quintana03d94902009-05-22 14:23:31 -0700545 * Add a ContentValues back reference.
546 * A column value from the back references takes precedence over a value specified in
547 * {@link #withValues}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700548 * This can only be used with builders of type insert, update, or assert.
Fred Quintana03d94902009-05-22 14:23:31 -0700549 * @return this builder, to allow for chaining.
550 */
551 public Builder withValueBackReference(String key, int previousResult) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700552 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintana03d94902009-05-22 14:23:31 -0700553 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700554 "only inserts, updates, and asserts can have value back-references");
Fred Quintana03d94902009-05-22 14:23:31 -0700555 }
556 if (mValuesBackReferences == null) {
557 mValuesBackReferences = new ContentValues();
558 }
559 mValuesBackReferences.put(key, previousResult);
560 return this;
561 }
562
563 /**
564 * Add a back references as a selection arg. Any value at that index of the selection arg
Fred Quintana89437372009-05-15 15:10:40 -0700565 * that was specified by {@link #withSelection} will be overwritten.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700566 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700567 * @return this builder, to allow for chaining.
568 */
Fred Quintana03d94902009-05-22 14:23:31 -0700569 public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700570 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
571 throw new IllegalArgumentException("only updates, deletes, and asserts "
572 + "can have selection back-references");
Fred Quintanace31b232009-05-04 16:01:15 -0700573 }
Fred Quintana03d94902009-05-22 14:23:31 -0700574 if (mSelectionArgsBackReferences == null) {
575 mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
576 }
577 mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
Fred Quintanace31b232009-05-04 16:01:15 -0700578 return this;
579 }
580
581 /**
582 * The ContentValues to use. This may be null. These values may be overwritten by
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700583 * the corresponding value specified by {@link #withValueBackReference} or by
584 * future calls to {@link #withValues} or {@link #withValue}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700585 * This can only be used with builders of type insert, update, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700586 * @return this builder, to allow for chaining.
587 */
588 public Builder withValues(ContentValues values) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700589 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
590 throw new IllegalArgumentException(
591 "only inserts, updates, and asserts can have values");
Fred Quintanace31b232009-05-04 16:01:15 -0700592 }
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700593 if (mValues == null) {
594 mValues = new ContentValues();
595 }
596 mValues.putAll(values);
Fred Quintanace31b232009-05-04 16:01:15 -0700597 return this;
598 }
599
600 /**
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700601 * A value to insert or update. This value may be overwritten by
602 * the corresponding value specified by {@link #withValueBackReference}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700603 * This can only be used with builders of type insert, update, or assert.
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700604 * @param key the name of this value
605 * @param value the value itself. the type must be acceptable for insertion by
606 * {@link ContentValues#put}
607 * @return this builder, to allow for chaining.
608 */
609 public Builder withValue(String key, Object value) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700610 if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700611 throw new IllegalArgumentException("only inserts and updates can have values");
612 }
613 if (mValues == null) {
614 mValues = new ContentValues();
615 }
616 if (value == null) {
617 mValues.putNull(key);
618 } else if (value instanceof String) {
619 mValues.put(key, (String) value);
620 } else if (value instanceof Byte) {
621 mValues.put(key, (Byte) value);
622 } else if (value instanceof Short) {
623 mValues.put(key, (Short) value);
624 } else if (value instanceof Integer) {
625 mValues.put(key, (Integer) value);
626 } else if (value instanceof Long) {
627 mValues.put(key, (Long) value);
628 } else if (value instanceof Float) {
629 mValues.put(key, (Float) value);
630 } else if (value instanceof Double) {
631 mValues.put(key, (Double) value);
632 } else if (value instanceof Boolean) {
633 mValues.put(key, (Boolean) value);
634 } else if (value instanceof byte[]) {
635 mValues.put(key, (byte[]) value);
636 } else {
637 throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
638 }
639 return this;
640 }
Jeff Sharkeybc254072009-07-28 20:39:35 -0700641
Fred Quintanad8dfeb52009-06-04 10:28:49 -0700642 /**
Fred Quintanace31b232009-05-04 16:01:15 -0700643 * The selection and arguments to use. An occurrence of '?' in the selection will be
644 * replaced with the corresponding occurence of the selection argument. Any of the
645 * selection arguments may be overwritten by a selection argument back reference as
Fred Quintana03d94902009-05-22 14:23:31 -0700646 * specified by {@link #withSelectionBackReference}.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700647 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700648 * @return this builder, to allow for chaining.
649 */
650 public Builder withSelection(String selection, String[] selectionArgs) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700651 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700652 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700653 "only updates, deletes, and asserts can have selections");
Fred Quintanace31b232009-05-04 16:01:15 -0700654 }
655 mSelection = selection;
Jeff Sharkey824838d2009-10-29 16:39:36 -0700656 if (selectionArgs == null) {
657 mSelectionArgs = null;
658 } else {
659 mSelectionArgs = new String[selectionArgs.length];
660 System.arraycopy(selectionArgs, 0, mSelectionArgs, 0, selectionArgs.length);
661 }
Fred Quintanace31b232009-05-04 16:01:15 -0700662 return this;
663 }
664
665 /**
Jeff Brown764e95e2015-06-10 17:16:05 -0700666 * If set then if the number of rows affected by this operation does not match
Fred Quintanace31b232009-05-04 16:01:15 -0700667 * this count {@link OperationApplicationException} will be throw.
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700668 * This can only be used with builders of type update, delete, or assert.
Fred Quintanace31b232009-05-04 16:01:15 -0700669 * @return this builder, to allow for chaining.
670 */
671 public Builder withExpectedCount(int count) {
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700672 if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
Fred Quintanace31b232009-05-04 16:01:15 -0700673 throw new IllegalArgumentException(
Jeff Sharkey08b75b12009-08-01 20:13:45 -0700674 "only updates, deletes, and asserts can have expected counts");
Fred Quintanace31b232009-05-04 16:01:15 -0700675 }
676 mExpectedCount = count;
677 return this;
678 }
Fred Quintana56f67d22009-08-28 14:36:42 -0700679
Jeff Brown764e95e2015-06-10 17:16:05 -0700680 /**
681 * If set to true then the operation allows yielding the database to other transactions
682 * if the database is contended.
683 * @return this builder, to allow for chaining.
684 * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
685 */
Fred Quintana56f67d22009-08-28 14:36:42 -0700686 public Builder withYieldAllowed(boolean yieldAllowed) {
687 mYieldAllowed = yieldAllowed;
688 return this;
689 }
Fred Quintanace31b232009-05-04 16:01:15 -0700690 }
691}