PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/core/java/android/content/ContentProviderOperation.java

https://gitlab.com/drgroovestarr/frameworks_base
Java | 691 lines | 429 code | 47 blank | 215 comment | 176 complexity | b725bb0bb04c36e2e2b2be2b4c0b7f60 MD5 | raw file
  1. /*
  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. package android.content;
  17. import android.content.ContentProvider;
  18. import android.database.Cursor;
  19. import android.net.Uri;
  20. import android.os.Parcel;
  21. import android.os.Parcelable;
  22. import android.text.TextUtils;
  23. import android.util.Log;
  24. import java.util.ArrayList;
  25. import java.util.HashMap;
  26. import java.util.Map;
  27. /**
  28. * Represents a single operation to be performed as part of a batch of operations.
  29. *
  30. * @see ContentProvider#applyBatch(ArrayList)
  31. */
  32. public class ContentProviderOperation implements Parcelable {
  33. /** @hide exposed for unit tests */
  34. public final static int TYPE_INSERT = 1;
  35. /** @hide exposed for unit tests */
  36. public final static int TYPE_UPDATE = 2;
  37. /** @hide exposed for unit tests */
  38. public final static int TYPE_DELETE = 3;
  39. /** @hide exposed for unit tests */
  40. public final static int TYPE_ASSERT = 4;
  41. private final int mType;
  42. private final Uri mUri;
  43. private final String mSelection;
  44. private final String[] mSelectionArgs;
  45. private final ContentValues mValues;
  46. private final Integer mExpectedCount;
  47. private final ContentValues mValuesBackReferences;
  48. private final Map<Integer, Integer> mSelectionArgsBackReferences;
  49. private final boolean mYieldAllowed;
  50. private final static String TAG = "ContentProviderOperation";
  51. /**
  52. * Creates a {@link ContentProviderOperation} by copying the contents of a
  53. * {@link Builder}.
  54. */
  55. private ContentProviderOperation(Builder builder) {
  56. mType = builder.mType;
  57. mUri = builder.mUri;
  58. mValues = builder.mValues;
  59. mSelection = builder.mSelection;
  60. mSelectionArgs = builder.mSelectionArgs;
  61. mExpectedCount = builder.mExpectedCount;
  62. mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
  63. mValuesBackReferences = builder.mValuesBackReferences;
  64. mYieldAllowed = builder.mYieldAllowed;
  65. }
  66. private ContentProviderOperation(Parcel source) {
  67. mType = source.readInt();
  68. mUri = Uri.CREATOR.createFromParcel(source);
  69. mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
  70. mSelection = source.readInt() != 0 ? source.readString() : null;
  71. mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
  72. mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
  73. mValuesBackReferences = source.readInt() != 0
  74. ? ContentValues.CREATOR.createFromParcel(source)
  75. : null;
  76. mSelectionArgsBackReferences = source.readInt() != 0
  77. ? new HashMap<Integer, Integer>()
  78. : null;
  79. if (mSelectionArgsBackReferences != null) {
  80. final int count = source.readInt();
  81. for (int i = 0; i < count; i++) {
  82. mSelectionArgsBackReferences.put(source.readInt(), source.readInt());
  83. }
  84. }
  85. mYieldAllowed = source.readInt() != 0;
  86. }
  87. /** @hide */
  88. public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
  89. mType = cpo.mType;
  90. if (removeUserIdFromUri) {
  91. mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
  92. } else {
  93. mUri = cpo.mUri;
  94. }
  95. mValues = cpo.mValues;
  96. mSelection = cpo.mSelection;
  97. mSelectionArgs = cpo.mSelectionArgs;
  98. mExpectedCount = cpo.mExpectedCount;
  99. mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
  100. mValuesBackReferences = cpo.mValuesBackReferences;
  101. mYieldAllowed = cpo.mYieldAllowed;
  102. }
  103. /** @hide */
  104. public ContentProviderOperation getWithoutUserIdInUri() {
  105. if (ContentProvider.uriHasUserId(mUri)) {
  106. return new ContentProviderOperation(this, true);
  107. }
  108. return this;
  109. }
  110. public void writeToParcel(Parcel dest, int flags) {
  111. dest.writeInt(mType);
  112. Uri.writeToParcel(dest, mUri);
  113. if (mValues != null) {
  114. dest.writeInt(1);
  115. mValues.writeToParcel(dest, 0);
  116. } else {
  117. dest.writeInt(0);
  118. }
  119. if (mSelection != null) {
  120. dest.writeInt(1);
  121. dest.writeString(mSelection);
  122. } else {
  123. dest.writeInt(0);
  124. }
  125. if (mSelectionArgs != null) {
  126. dest.writeInt(1);
  127. dest.writeStringArray(mSelectionArgs);
  128. } else {
  129. dest.writeInt(0);
  130. }
  131. if (mExpectedCount != null) {
  132. dest.writeInt(1);
  133. dest.writeInt(mExpectedCount);
  134. } else {
  135. dest.writeInt(0);
  136. }
  137. if (mValuesBackReferences != null) {
  138. dest.writeInt(1);
  139. mValuesBackReferences.writeToParcel(dest, 0);
  140. } else {
  141. dest.writeInt(0);
  142. }
  143. if (mSelectionArgsBackReferences != null) {
  144. dest.writeInt(1);
  145. dest.writeInt(mSelectionArgsBackReferences.size());
  146. for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) {
  147. dest.writeInt(entry.getKey());
  148. dest.writeInt(entry.getValue());
  149. }
  150. } else {
  151. dest.writeInt(0);
  152. }
  153. dest.writeInt(mYieldAllowed ? 1 : 0);
  154. }
  155. /**
  156. * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}.
  157. * @param uri The {@link Uri} that is the target of the insert.
  158. * @return a {@link Builder}
  159. */
  160. public static Builder newInsert(Uri uri) {
  161. return new Builder(TYPE_INSERT, uri);
  162. }
  163. /**
  164. * Create a {@link Builder} suitable for building an update {@link ContentProviderOperation}.
  165. * @param uri The {@link Uri} that is the target of the update.
  166. * @return a {@link Builder}
  167. */
  168. public static Builder newUpdate(Uri uri) {
  169. return new Builder(TYPE_UPDATE, uri);
  170. }
  171. /**
  172. * Create a {@link Builder} suitable for building a delete {@link ContentProviderOperation}.
  173. * @param uri The {@link Uri} that is the target of the delete.
  174. * @return a {@link Builder}
  175. */
  176. public static Builder newDelete(Uri uri) {
  177. return new Builder(TYPE_DELETE, uri);
  178. }
  179. /**
  180. * Create a {@link Builder} suitable for building a
  181. * {@link ContentProviderOperation} to assert a set of values as provided
  182. * through {@link Builder#withValues(ContentValues)}.
  183. */
  184. public static Builder newAssertQuery(Uri uri) {
  185. return new Builder(TYPE_ASSERT, uri);
  186. }
  187. /**
  188. * Gets the Uri for the target of the operation.
  189. */
  190. public Uri getUri() {
  191. return mUri;
  192. }
  193. /**
  194. * Returns true if the operation allows yielding the database to other transactions
  195. * if the database is contended.
  196. *
  197. * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
  198. */
  199. public boolean isYieldAllowed() {
  200. return mYieldAllowed;
  201. }
  202. /** @hide exposed for unit tests */
  203. public int getType() {
  204. return mType;
  205. }
  206. /**
  207. * Returns true if the operation represents an insertion.
  208. *
  209. * @see #newInsert
  210. */
  211. public boolean isInsert() {
  212. return mType == TYPE_INSERT;
  213. }
  214. /**
  215. * Returns true if the operation represents a deletion.
  216. *
  217. * @see #newDelete
  218. */
  219. public boolean isDelete() {
  220. return mType == TYPE_DELETE;
  221. }
  222. /**
  223. * Returns true if the operation represents an update.
  224. *
  225. * @see #newUpdate
  226. */
  227. public boolean isUpdate() {
  228. return mType == TYPE_UPDATE;
  229. }
  230. /**
  231. * Returns true if the operation represents an assert query.
  232. *
  233. * @see #newAssertQuery
  234. */
  235. public boolean isAssertQuery() {
  236. return mType == TYPE_ASSERT;
  237. }
  238. /**
  239. * Returns true if the operation represents an insertion, deletion, or update.
  240. *
  241. * @see #isInsert
  242. * @see #isDelete
  243. * @see #isUpdate
  244. */
  245. public boolean isWriteOperation() {
  246. return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
  247. }
  248. /**
  249. * Returns true if the operation represents an assert query.
  250. *
  251. * @see #isAssertQuery
  252. */
  253. public boolean isReadOperation() {
  254. return mType == TYPE_ASSERT;
  255. }
  256. /**
  257. * Applies this operation using the given provider. The backRefs array is used to resolve any
  258. * back references that were requested using
  259. * {@link Builder#withValueBackReferences(ContentValues)} and
  260. * {@link Builder#withSelectionBackReference}.
  261. * @param provider the {@link ContentProvider} on which this batch is applied
  262. * @param backRefs a {@link ContentProviderResult} array that will be consulted
  263. * to resolve any requested back references.
  264. * @param numBackRefs the number of valid results on the backRefs array.
  265. * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
  266. * row if this was an insert otherwise the number of rows affected.
  267. * @throws OperationApplicationException thrown if either the insert fails or
  268. * if the number of rows affected didn't match the expected count
  269. */
  270. public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
  271. int numBackRefs) throws OperationApplicationException {
  272. ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
  273. String[] selectionArgs =
  274. resolveSelectionArgsBackReferences(backRefs, numBackRefs);
  275. if (mType == TYPE_INSERT) {
  276. Uri newUri = provider.insert(mUri, values);
  277. if (newUri == null) {
  278. throw new OperationApplicationException("insert failed");
  279. }
  280. return new ContentProviderResult(newUri);
  281. }
  282. int numRows;
  283. if (mType == TYPE_DELETE) {
  284. numRows = provider.delete(mUri, mSelection, selectionArgs);
  285. } else if (mType == TYPE_UPDATE) {
  286. numRows = provider.update(mUri, values, mSelection, selectionArgs);
  287. } else if (mType == TYPE_ASSERT) {
  288. // Assert that all rows match expected values
  289. String[] projection = null;
  290. if (values != null) {
  291. // Build projection map from expected values
  292. final ArrayList<String> projectionList = new ArrayList<String>();
  293. for (Map.Entry<String, Object> entry : values.valueSet()) {
  294. projectionList.add(entry.getKey());
  295. }
  296. projection = projectionList.toArray(new String[projectionList.size()]);
  297. }
  298. final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
  299. try {
  300. numRows = cursor.getCount();
  301. if (projection != null) {
  302. while (cursor.moveToNext()) {
  303. for (int i = 0; i < projection.length; i++) {
  304. final String cursorValue = cursor.getString(i);
  305. final String expectedValue = values.getAsString(projection[i]);
  306. if (!TextUtils.equals(cursorValue, expectedValue)) {
  307. // Throw exception when expected values don't match
  308. Log.e(TAG, this.toString());
  309. throw new OperationApplicationException("Found value " + cursorValue
  310. + " when expected " + expectedValue + " for column "
  311. + projection[i]);
  312. }
  313. }
  314. }
  315. }
  316. } finally {
  317. cursor.close();
  318. }
  319. } else {
  320. Log.e(TAG, this.toString());
  321. throw new IllegalStateException("bad type, " + mType);
  322. }
  323. if (mExpectedCount != null && mExpectedCount != numRows) {
  324. Log.e(TAG, this.toString());
  325. throw new OperationApplicationException("wrong number of rows: " + numRows);
  326. }
  327. return new ContentProviderResult(numRows);
  328. }
  329. /**
  330. * The ContentValues back references are represented as a ContentValues object where the
  331. * key refers to a column and the value is an index of the back reference whose
  332. * valued should be associated with the column.
  333. * <p>
  334. * This is intended to be a private method but it is exposed for
  335. * unit testing purposes
  336. * @param backRefs an array of previous results
  337. * @param numBackRefs the number of valid previous results in backRefs
  338. * @return the ContentValues that should be used in this operation application after
  339. * expansion of back references. This can be called if either mValues or mValuesBackReferences
  340. * is null
  341. */
  342. public ContentValues resolveValueBackReferences(
  343. ContentProviderResult[] backRefs, int numBackRefs) {
  344. if (mValuesBackReferences == null) {
  345. return mValues;
  346. }
  347. final ContentValues values;
  348. if (mValues == null) {
  349. values = new ContentValues();
  350. } else {
  351. values = new ContentValues(mValues);
  352. }
  353. for (Map.Entry<String, Object> entry : mValuesBackReferences.valueSet()) {
  354. String key = entry.getKey();
  355. Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
  356. if (backRefIndex == null) {
  357. Log.e(TAG, this.toString());
  358. throw new IllegalArgumentException("values backref " + key + " is not an integer");
  359. }
  360. values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
  361. }
  362. return values;
  363. }
  364. /**
  365. * The Selection Arguments back references are represented as a Map of Integer->Integer where
  366. * the key is an index into the selection argument array (see {@link Builder#withSelection})
  367. * and the value is the index of the previous result that should be used for that selection
  368. * argument array slot.
  369. * <p>
  370. * This is intended to be a private method but it is exposed for
  371. * unit testing purposes
  372. * @param backRefs an array of previous results
  373. * @param numBackRefs the number of valid previous results in backRefs
  374. * @return the ContentValues that should be used in this operation application after
  375. * expansion of back references. This can be called if either mValues or mValuesBackReferences
  376. * is null
  377. */
  378. public String[] resolveSelectionArgsBackReferences(
  379. ContentProviderResult[] backRefs, int numBackRefs) {
  380. if (mSelectionArgsBackReferences == null) {
  381. return mSelectionArgs;
  382. }
  383. String[] newArgs = new String[mSelectionArgs.length];
  384. System.arraycopy(mSelectionArgs, 0, newArgs, 0, mSelectionArgs.length);
  385. for (Map.Entry<Integer, Integer> selectionArgBackRef
  386. : mSelectionArgsBackReferences.entrySet()) {
  387. final Integer selectionArgIndex = selectionArgBackRef.getKey();
  388. final int backRefIndex = selectionArgBackRef.getValue();
  389. newArgs[selectionArgIndex] =
  390. String.valueOf(backRefToValue(backRefs, numBackRefs, backRefIndex));
  391. }
  392. return newArgs;
  393. }
  394. @Override
  395. public String toString() {
  396. return "mType: " + mType + ", mUri: " + mUri +
  397. ", mSelection: " + mSelection +
  398. ", mExpectedCount: " + mExpectedCount +
  399. ", mYieldAllowed: " + mYieldAllowed +
  400. ", mValues: " + mValues +
  401. ", mValuesBackReferences: " + mValuesBackReferences +
  402. ", mSelectionArgsBackReferences: " + mSelectionArgsBackReferences;
  403. }
  404. /**
  405. * Return the string representation of the requested back reference.
  406. * @param backRefs an array of results
  407. * @param numBackRefs the number of items in the backRefs array that are valid
  408. * @param backRefIndex which backRef to be used
  409. * @throws ArrayIndexOutOfBoundsException thrown if the backRefIndex is larger than
  410. * the numBackRefs
  411. * @return the string representation of the requested back reference.
  412. */
  413. private long backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
  414. Integer backRefIndex) {
  415. if (backRefIndex >= numBackRefs) {
  416. Log.e(TAG, this.toString());
  417. throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
  418. + " but there are only " + numBackRefs + " back refs");
  419. }
  420. ContentProviderResult backRef = backRefs[backRefIndex];
  421. long backRefValue;
  422. if (backRef.uri != null) {
  423. backRefValue = ContentUris.parseId(backRef.uri);
  424. } else {
  425. backRefValue = backRef.count;
  426. }
  427. return backRefValue;
  428. }
  429. public int describeContents() {
  430. return 0;
  431. }
  432. public static final Creator<ContentProviderOperation> CREATOR =
  433. new Creator<ContentProviderOperation>() {
  434. public ContentProviderOperation createFromParcel(Parcel source) {
  435. return new ContentProviderOperation(source);
  436. }
  437. public ContentProviderOperation[] newArray(int size) {
  438. return new ContentProviderOperation[size];
  439. }
  440. };
  441. /**
  442. * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
  443. * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
  444. * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
  445. * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
  446. * {@link ContentProviderOperation#newAssertQuery(Uri)}. The withXXX methods
  447. * can then be used to add parameters to the builder. See the specific methods to find for
  448. * which {@link Builder} type each is allowed. Call {@link #build} to create the
  449. * {@link ContentProviderOperation} once all the parameters have been supplied.
  450. */
  451. public static class Builder {
  452. private final int mType;
  453. private final Uri mUri;
  454. private String mSelection;
  455. private String[] mSelectionArgs;
  456. private ContentValues mValues;
  457. private Integer mExpectedCount;
  458. private ContentValues mValuesBackReferences;
  459. private Map<Integer, Integer> mSelectionArgsBackReferences;
  460. private boolean mYieldAllowed;
  461. /** Create a {@link Builder} of a given type. The uri must not be null. */
  462. private Builder(int type, Uri uri) {
  463. if (uri == null) {
  464. throw new IllegalArgumentException("uri must not be null");
  465. }
  466. mType = type;
  467. mUri = uri;
  468. }
  469. /** Create a ContentProviderOperation from this {@link Builder}. */
  470. public ContentProviderOperation build() {
  471. if (mType == TYPE_UPDATE) {
  472. if ((mValues == null || mValues.isEmpty())
  473. && (mValuesBackReferences == null || mValuesBackReferences.isEmpty())) {
  474. throw new IllegalArgumentException("Empty values");
  475. }
  476. }
  477. if (mType == TYPE_ASSERT) {
  478. if ((mValues == null || mValues.isEmpty())
  479. && (mValuesBackReferences == null || mValuesBackReferences.isEmpty())
  480. && (mExpectedCount == null)) {
  481. throw new IllegalArgumentException("Empty values");
  482. }
  483. }
  484. return new ContentProviderOperation(this);
  485. }
  486. /**
  487. * Add a {@link ContentValues} of back references. The key is the name of the column
  488. * and the value is an integer that is the index of the previous result whose
  489. * value should be used for the column. The value is added as a {@link String}.
  490. * A column value from the back references takes precedence over a value specified in
  491. * {@link #withValues}.
  492. * This can only be used with builders of type insert, update, or assert.
  493. * @return this builder, to allow for chaining.
  494. */
  495. public Builder withValueBackReferences(ContentValues backReferences) {
  496. if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
  497. throw new IllegalArgumentException(
  498. "only inserts, updates, and asserts can have value back-references");
  499. }
  500. mValuesBackReferences = backReferences;
  501. return this;
  502. }
  503. /**
  504. * Add a ContentValues back reference.
  505. * A column value from the back references takes precedence over a value specified in
  506. * {@link #withValues}.
  507. * This can only be used with builders of type insert, update, or assert.
  508. * @return this builder, to allow for chaining.
  509. */
  510. public Builder withValueBackReference(String key, int previousResult) {
  511. if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
  512. throw new IllegalArgumentException(
  513. "only inserts, updates, and asserts can have value back-references");
  514. }
  515. if (mValuesBackReferences == null) {
  516. mValuesBackReferences = new ContentValues();
  517. }
  518. mValuesBackReferences.put(key, previousResult);
  519. return this;
  520. }
  521. /**
  522. * Add a back references as a selection arg. Any value at that index of the selection arg
  523. * that was specified by {@link #withSelection} will be overwritten.
  524. * This can only be used with builders of type update, delete, or assert.
  525. * @return this builder, to allow for chaining.
  526. */
  527. public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
  528. if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
  529. throw new IllegalArgumentException("only updates, deletes, and asserts "
  530. + "can have selection back-references");
  531. }
  532. if (mSelectionArgsBackReferences == null) {
  533. mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
  534. }
  535. mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
  536. return this;
  537. }
  538. /**
  539. * The ContentValues to use. This may be null. These values may be overwritten by
  540. * the corresponding value specified by {@link #withValueBackReference} or by
  541. * future calls to {@link #withValues} or {@link #withValue}.
  542. * This can only be used with builders of type insert, update, or assert.
  543. * @return this builder, to allow for chaining.
  544. */
  545. public Builder withValues(ContentValues values) {
  546. if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
  547. throw new IllegalArgumentException(
  548. "only inserts, updates, and asserts can have values");
  549. }
  550. if (mValues == null) {
  551. mValues = new ContentValues();
  552. }
  553. mValues.putAll(values);
  554. return this;
  555. }
  556. /**
  557. * A value to insert or update. This value may be overwritten by
  558. * the corresponding value specified by {@link #withValueBackReference}.
  559. * This can only be used with builders of type insert, update, or assert.
  560. * @param key the name of this value
  561. * @param value the value itself. the type must be acceptable for insertion by
  562. * {@link ContentValues#put}
  563. * @return this builder, to allow for chaining.
  564. */
  565. public Builder withValue(String key, Object value) {
  566. if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
  567. throw new IllegalArgumentException("only inserts and updates can have values");
  568. }
  569. if (mValues == null) {
  570. mValues = new ContentValues();
  571. }
  572. if (value == null) {
  573. mValues.putNull(key);
  574. } else if (value instanceof String) {
  575. mValues.put(key, (String) value);
  576. } else if (value instanceof Byte) {
  577. mValues.put(key, (Byte) value);
  578. } else if (value instanceof Short) {
  579. mValues.put(key, (Short) value);
  580. } else if (value instanceof Integer) {
  581. mValues.put(key, (Integer) value);
  582. } else if (value instanceof Long) {
  583. mValues.put(key, (Long) value);
  584. } else if (value instanceof Float) {
  585. mValues.put(key, (Float) value);
  586. } else if (value instanceof Double) {
  587. mValues.put(key, (Double) value);
  588. } else if (value instanceof Boolean) {
  589. mValues.put(key, (Boolean) value);
  590. } else if (value instanceof byte[]) {
  591. mValues.put(key, (byte[]) value);
  592. } else {
  593. throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
  594. }
  595. return this;
  596. }
  597. /**
  598. * The selection and arguments to use. An occurrence of '?' in the selection will be
  599. * replaced with the corresponding occurence of the selection argument. Any of the
  600. * selection arguments may be overwritten by a selection argument back reference as
  601. * specified by {@link #withSelectionBackReference}.
  602. * This can only be used with builders of type update, delete, or assert.
  603. * @return this builder, to allow for chaining.
  604. */
  605. public Builder withSelection(String selection, String[] selectionArgs) {
  606. if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
  607. throw new IllegalArgumentException(
  608. "only updates, deletes, and asserts can have selections");
  609. }
  610. mSelection = selection;
  611. if (selectionArgs == null) {
  612. mSelectionArgs = null;
  613. } else {
  614. mSelectionArgs = new String[selectionArgs.length];
  615. System.arraycopy(selectionArgs, 0, mSelectionArgs, 0, selectionArgs.length);
  616. }
  617. return this;
  618. }
  619. /**
  620. * If set then if the number of rows affected by this operation does not match
  621. * this count {@link OperationApplicationException} will be throw.
  622. * This can only be used with builders of type update, delete, or assert.
  623. * @return this builder, to allow for chaining.
  624. */
  625. public Builder withExpectedCount(int count) {
  626. if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
  627. throw new IllegalArgumentException(
  628. "only updates, deletes, and asserts can have expected counts");
  629. }
  630. mExpectedCount = count;
  631. return this;
  632. }
  633. /**
  634. * If set to true then the operation allows yielding the database to other transactions
  635. * if the database is contended.
  636. * @return this builder, to allow for chaining.
  637. * @see android.database.sqlite.SQLiteDatabase#yieldIfContendedSafely()
  638. */
  639. public Builder withYieldAllowed(boolean yieldAllowed) {
  640. mYieldAllowed = yieldAllowed;
  641. return this;
  642. }
  643. }
  644. }