PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/frameworks/base/core/java/android/content/ContentProviderOperation.java

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