Home | History | Annotate | Download | only in picasa
      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