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 17 package com.cooliris.picasa; 18 19 import java.util.ArrayList; 20 21 import android.content.ContentProvider; 22 import android.content.ContentResolver; 23 import android.content.ContentValues; 24 import android.content.UriMatcher; 25 import android.database.Cursor; 26 import android.database.SQLException; 27 import android.database.sqlite.SQLiteDatabase; 28 import android.database.sqlite.SQLiteOpenHelper; 29 import android.net.Uri; 30 import android.text.TextUtils; 31 32 public class TableContentProvider extends ContentProvider { 33 private static final String NULL_COLUMN_HACK = "_id"; 34 protected SQLiteOpenHelper mDatabase = null; 35 private final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 36 private final ArrayList<Mapping> mMappings = new ArrayList<Mapping>(); 37 38 public void setDatabase(SQLiteOpenHelper database) { 39 mDatabase = database; 40 } 41 42 public void addMapping(String authority, String path, String mimeSubtype, EntrySchema table) { 43 // Add the table URI mapping. 44 ArrayList<Mapping> mappings = mMappings; 45 UriMatcher matcher = mUriMatcher; 46 matcher.addURI(authority, path, mappings.size()); 47 mappings.add(new Mapping(table, mimeSubtype, false)); 48 49 // Add the row URI mapping. 50 matcher.addURI(authority, path + "/#", mappings.size()); 51 mappings.add(new Mapping(table, mimeSubtype, true)); 52 } 53 54 @Override 55 public boolean onCreate() { 56 // The database may not be loaded yet since attachInfo() has not been 57 // called, so we cannot 58 // check that the database opened successfully. Returns true 59 // optimistically. 60 return true; 61 } 62 63 @Override 64 public String getType(Uri uri) { 65 // Resolve the URI. 66 int match = mUriMatcher.match(uri); 67 if (match == UriMatcher.NO_MATCH) { 68 throw new IllegalArgumentException("Invalid URI: " + uri); 69 } 70 71 // Combine the standard type with the user-provided subtype. 72 Mapping mapping = mMappings.get(match); 73 String prefix = mapping.hasId ? ContentResolver.CURSOR_ITEM_BASE_TYPE : ContentResolver.CURSOR_DIR_BASE_TYPE; 74 return prefix + "/" + mapping.mimeSubtype; 75 } 76 77 @Override 78 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 79 // Resolve the URI. 80 int match = mUriMatcher.match(uri); 81 if (match == UriMatcher.NO_MATCH) { 82 throw new IllegalArgumentException("Invalid URI: " + uri); 83 } 84 85 // Add the ID predicate if needed. 86 Mapping mapping = mMappings.get(match); 87 if (mapping.hasId) { 88 selection = whereWithId(uri, selection); 89 } 90 91 // System.out.println("QUERY " + uri + " WHERE (" + selection + ")"); 92 93 // Run the query. 94 String tableName = mapping.table.getTableName(); 95 Cursor cursor = mDatabase.getReadableDatabase().query(tableName, projection, selection, selectionArgs, null, null, 96 sortOrder); 97 cursor.setNotificationUri(getContext().getContentResolver(), uri); 98 return cursor; 99 } 100 101 @Override 102 public Uri insert(Uri uri, ContentValues values) { 103 // Resolve the URI. 104 int match = mUriMatcher.match(uri); 105 Mapping mapping = match != UriMatcher.NO_MATCH ? mMappings.get(match) : null; 106 if (mapping == null || mapping.hasId) { 107 throw new IllegalArgumentException("Invalid URI: " + uri); 108 } 109 110 // Insert into the database, notify observers, and return the qualified 111 // URI. 112 String tableName = mapping.table.getTableName(); 113 long rowId = mDatabase.getWritableDatabase().insert(tableName, NULL_COLUMN_HACK, values); 114 if (rowId > 0) { 115 notifyChange(uri); 116 return Uri.withAppendedPath(uri, Long.toString(rowId)); 117 } else { 118 throw new SQLException("Failed to insert row at: " + uri); 119 } 120 } 121 122 @Override 123 public int bulkInsert(Uri uri, ContentValues[] values) { 124 // Resolve the URI. 125 int match = mUriMatcher.match(uri); 126 Mapping mapping = match != UriMatcher.NO_MATCH ? mMappings.get(match) : null; 127 if (mapping == null || mapping.hasId) { 128 throw new IllegalArgumentException("Invalid URI: " + uri); 129 } 130 131 // Insert all rows into the database and notify observers. 132 String tableName = mapping.table.getTableName(); 133 SQLiteDatabase database = mDatabase.getWritableDatabase(); 134 int numInserted = 0; 135 try { 136 int length = values.length; 137 database.beginTransaction(); 138 for (int i = 0; i != length; ++i) { 139 database.insert(tableName, NULL_COLUMN_HACK, values[i]); 140 } 141 database.setTransactionSuccessful(); 142 numInserted = length; 143 } finally { 144 database.endTransaction(); 145 } 146 notifyChange(uri); 147 return numInserted; 148 } 149 150 @Override 151 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 152 // Resolve the URI. 153 int match = mUriMatcher.match(uri); 154 if (match == UriMatcher.NO_MATCH) { 155 throw new IllegalArgumentException("Invalid URI: " + uri); 156 } 157 158 // Add the ID predicate if needed. 159 Mapping mapping = mMappings.get(match); 160 if (mapping.hasId) { 161 selection = whereWithId(uri, selection); 162 } 163 164 // Update the item(s) and broadcast a change notification. 165 SQLiteDatabase db = mDatabase.getWritableDatabase(); 166 String tableName = mapping.table.getTableName(); 167 int count = db.update(tableName, values, selection, selectionArgs); 168 notifyChange(uri); 169 return count; 170 } 171 172 @Override 173 public int delete(Uri uri, String selection, String[] selectionArgs) { 174 // Resolve the URI. 175 int match = mUriMatcher.match(uri); 176 if (match == UriMatcher.NO_MATCH) { 177 throw new IllegalArgumentException("Invalid URI: " + uri); 178 } 179 180 // Add the ID predicate if needed. 181 Mapping mapping = mMappings.get(match); 182 if (mapping.hasId) { 183 selection = whereWithId(uri, selection); 184 } 185 186 // Delete the item(s) and broadcast a change notification. 187 SQLiteDatabase db = mDatabase.getWritableDatabase(); 188 String tableName = mapping.table.getTableName(); 189 int count = db.delete(tableName, selection, selectionArgs); 190 notifyChange(uri); 191 return count; 192 } 193 194 private final String whereWithId(Uri uri, String selection) { 195 String id = uri.getPathSegments().get(1); 196 StringBuilder where = new StringBuilder("_id="); 197 where.append(id); 198 if (!TextUtils.isEmpty(selection)) { 199 where.append(" AND ("); 200 where.append(selection); 201 where.append(')'); 202 } 203 return where.toString(); 204 } 205 206 private final void notifyChange(Uri uri) { 207 getContext().getContentResolver().notifyChange(uri, null); 208 } 209 210 private static final class Mapping { 211 public EntrySchema table; 212 public String mimeSubtype; 213 public boolean hasId; 214 215 public Mapping(EntrySchema table, String mimeSubtype, boolean hasId) { 216 this.table = table; 217 this.mimeSubtype = mimeSubtype; 218 this.hasId = hasId; 219 } 220 } 221 } 222