| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.exchange.provider; |
| |
| import android.content.ContentProvider; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.UriMatcher; |
| import android.database.Cursor; |
| import android.database.MatrixCursor; |
| import android.net.Uri; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Set; |
| import java.util.Map.Entry; |
| |
| /** |
| * MockProvider is a ContentProvider that can be used to simulate the storage and retrieval of |
| * records from any ContentProvider, even if that ContentProvider does not exist in the caller's |
| * package. It is specifically designed to enable testing of sync adapters that create |
| * ContentProviderOperations (CPOs) that are then executed using ContentResolver.applyBatch() |
| * |
| * Why is this useful? Because we can't instantiate CalendarProvider or ContactsProvider from our |
| * package, as required by MockContentResolver.addProvider() |
| * |
| * Usage: |
| * ContentResolver.applyBatch(MockProvider.AUTHORITY, batch) will cause the CPOs to be executed, |
| * returning an array of ContentProviderResult; in the case of inserts, the result will include |
| * a Uri that can be used via query(). Note that the CPOs in the batch can contain references |
| * to any authority. |
| * |
| * query() does not allow non-null selection, selectionArgs, or sortOrder arguments; the |
| * presence of these will result in an UnsupportedOperationException |
| * |
| * insert() acts as expected, returning a Uri that can be directly used in a query |
| * |
| * delete() and update() do not allow non-null selection or selectionArgs arguments; the |
| * presence of these will result in an UnsupportedOperationException |
| * |
| * NOTE: When using any operation other than applyBatch, the Uri to be used must be created |
| * with MockProvider.uri(yourUri). This guarantees that the operation is sent to MockProvider |
| * |
| * NOTE: MockProvider only simulates direct storage/retrieval of rows; it does not (and can not) |
| * simulate other actions (e.g. creation of ancillary data) that the actual provider might |
| * perform |
| * |
| * NOTE: See MockProviderTests for usage examples |
| **/ |
| public class MockProvider extends ContentProvider { |
| public static final String AUTHORITY = "com.android.exchange.mock.provider"; |
| /*package*/ static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); |
| |
| /*package*/ static final int TABLE = 100; |
| /*package*/ static final int RECORD = 101; |
| |
| public static final String ID_COLUMN = "_id"; |
| |
| public MockProvider(Context context) { |
| super(context, null, null, null); |
| } |
| |
| public MockProvider() { |
| super(); |
| } |
| |
| // We'll store our values here |
| private HashMap<String, ContentValues> mMockStore = new HashMap<String, ContentValues>(); |
| // And we'll generate new id's from here |
| long mMockId = 1; |
| |
| /** |
| * Create a Uri for MockProvider from a given Uri |
| * @param uri the Uri from which the MockProvider Uri will be created |
| * @return a Uri that can be used with MockProvider |
| */ |
| public static Uri uri(Uri uri) { |
| return new Uri.Builder().scheme("content").authority(AUTHORITY) |
| .path(uri.getPath().substring(1)).build(); |
| } |
| |
| @Override |
| public int delete(Uri uri, String selection, String[] selectionArgs) { |
| if (selection != null || selectionArgs != null) { |
| throw new UnsupportedOperationException(); |
| } |
| String path = uri.getPath(); |
| if (mMockStore.containsKey(path)) { |
| mMockStore.remove(path); |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| @Override |
| public String getType(Uri uri) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Uri insert(Uri uri, ContentValues values) { |
| // Remove the leading slash |
| String table = uri.getPath().substring(1); |
| long id = mMockId++; |
| Uri newUri = new Uri.Builder().scheme("content").authority(AUTHORITY).path(table) |
| .appendPath(Long.toString(id)).build(); |
| // Remember to store the _id |
| values.put(ID_COLUMN, id); |
| mMockStore.put(newUri.getPath(), values); |
| int match = sURIMatcher.match(uri); |
| if (match == UriMatcher.NO_MATCH) { |
| sURIMatcher.addURI(AUTHORITY, table, TABLE); |
| sURIMatcher.addURI(AUTHORITY, table + "/#", RECORD); |
| } |
| return newUri; |
| } |
| |
| @Override |
| public boolean onCreate() { |
| return false; |
| } |
| |
| @Override |
| public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, |
| String sortOrder) { |
| if (selection != null || selectionArgs != null || sortOrder != null || projection == null) { |
| throw new UnsupportedOperationException(); |
| } |
| final int match = sURIMatcher.match(uri(uri)); |
| ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>(); |
| switch(match) { |
| case TABLE: |
| Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet(); |
| String prefix = uri.getPath() + "/"; |
| for (Entry<String, ContentValues> entry: entrySet) { |
| if (entry.getKey().startsWith(prefix)) { |
| valuesList.add(entry.getValue()); |
| } |
| } |
| break; |
| case RECORD: |
| ContentValues values = mMockStore.get(uri.getPath()); |
| if (values != null) { |
| valuesList.add(values); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown URI " + uri); |
| } |
| MatrixCursor cursor = new MatrixCursor(projection, 1); |
| for (ContentValues cv: valuesList) { |
| Object[] rowValues = new Object[projection.length]; |
| int i = 0; |
| for (String column: projection) { |
| rowValues[i++] = cv.get(column); |
| } |
| cursor.addRow(rowValues); |
| } |
| return cursor; |
| } |
| |
| @Override |
| public int update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs) { |
| if (selection != null || selectionArgs != null) { |
| throw new UnsupportedOperationException(); |
| } |
| final int match = sURIMatcher.match(uri(uri)); |
| ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>(); |
| String path = uri.getPath(); |
| switch(match) { |
| case TABLE: |
| Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet(); |
| String prefix = path + "/"; |
| for (Entry<String, ContentValues> entry: entrySet) { |
| if (entry.getKey().startsWith(prefix)) { |
| updateValuesList.add(entry.getValue()); |
| } |
| } |
| break; |
| case RECORD: |
| ContentValues cv = mMockStore.get(path); |
| if (cv != null) { |
| updateValuesList.add(cv); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown URI " + uri); |
| } |
| Set<Entry<String, Object>> newValuesSet = newValues.valueSet(); |
| for (Entry<String, Object> entry: newValuesSet) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| for (ContentValues targetValues: updateValuesList) { |
| if (value instanceof Integer) { |
| targetValues.put(key, (Integer)value); |
| } else if (value instanceof Long) { |
| targetValues.put(key, (Long)value); |
| } else if (value instanceof String) { |
| targetValues.put(key, (String)value); |
| } else if (value instanceof Boolean) { |
| targetValues.put(key, (Boolean)value); |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } |
| } |
| for (ContentValues targetValues: updateValuesList) { |
| mMockStore.put(path + "/" + targetValues.getAsLong(ID_COLUMN), targetValues); |
| } |
| return updateValuesList.size(); |
| } |
| } |