PageRenderTime 167ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

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

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