gnunet-android

GNUnet for Android
Log | Files | Refs | README

SQLiteCursor.java (9249B)


      1 /*
      2  * Copyright (C) 2006 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 ** Modified to support SQLite extensions by the SQLite developers: 
     18 ** sqlite-dev@sqlite.org.
     19 */
     20 
     21 package org.sqlite.database.sqlite;
     22 
     23 import android.database.AbstractWindowedCursor;
     24 import android.database.CursorWindow;
     25 import org.sqlite.database.DatabaseUtils;
     26 
     27 import android.os.StrictMode;
     28 import android.util.Log;
     29 
     30 import java.util.HashMap;
     31 import java.util.Map;
     32 
     33 /**
     34  * A Cursor implementation that exposes results from a query on a
     35  * {@link SQLiteDatabase}.
     36  *
     37  * SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
     38  * threads should perform its own synchronization when using the SQLiteCursor.
     39  */
     40 public class SQLiteCursor extends AbstractWindowedCursor {
     41     static final String TAG = "SQLiteCursor";
     42     static final int NO_COUNT = -1;
     43 
     44     /** The name of the table to edit */
     45     private final String mEditTable;
     46 
     47     /** The names of the columns in the rows */
     48     private final String[] mColumns;
     49 
     50     /** The query object for the cursor */
     51     private final SQLiteQuery mQuery;
     52 
     53     /** The compiled query this cursor came from */
     54     private final SQLiteCursorDriver mDriver;
     55 
     56     /** The number of rows in the cursor */
     57     private int mCount = NO_COUNT;
     58 
     59     /** The number of rows that can fit in the cursor window, 0 if unknown */
     60     private int mCursorWindowCapacity;
     61 
     62     /** A mapping of column names to column indices, to speed up lookups */
     63     private Map<String, Integer> mColumnNameMap;
     64 
     65     /**
     66      * Execute a query and provide access to its result set through a Cursor
     67      * interface. For a query such as: {@code SELECT name, birth, phone FROM
     68      * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
     69      * phone) would be in the projection argument and everything from
     70      * {@code FROM} onward would be in the params argument.
     71      *
     72      * @param db a reference to a Database object that is already constructed
     73      *     and opened. This param is not used any longer
     74      * @param editTable the name of the table used for this query
     75      * @param query the rest of the query terms
     76      *     cursor is finalized
     77      * @deprecated use {@link #SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)} instead
     78      */
     79     @Deprecated
     80     public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
     81             String editTable, SQLiteQuery query) {
     82         this(driver, editTable, query);
     83     }
     84 
     85     /**
     86      * Execute a query and provide access to its result set through a Cursor
     87      * interface. For a query such as: {@code SELECT name, birth, phone FROM
     88      * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
     89      * phone) would be in the projection argument and everything from
     90      * {@code FROM} onward would be in the params argument.
     91      *
     92      * @param editTable the name of the table used for this query
     93      * @param query the {@link SQLiteQuery} object associated with this cursor object.
     94      */
     95     public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
     96         if (query == null) {
     97             throw new IllegalArgumentException("query object cannot be null");
     98         }
     99         mDriver = driver;
    100         mEditTable = editTable;
    101         mColumnNameMap = null;
    102         mQuery = query;
    103 
    104         mColumns = query.getColumnNames();
    105     }
    106 
    107     /**
    108      * Get the database that this cursor is associated with.
    109      * @return the SQLiteDatabase that this cursor is associated with.
    110      */
    111     public SQLiteDatabase getDatabase() {
    112         return mQuery.getDatabase();
    113     }
    114 
    115     @Override
    116     public boolean onMove(int oldPosition, int newPosition) {
    117         // Make sure the row at newPosition is present in the window
    118         if (mWindow == null || newPosition < mWindow.getStartPosition() ||
    119                 newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
    120             fillWindow(newPosition);
    121         }
    122 
    123         return true;
    124     }
    125 
    126     @Override
    127     public int getCount() {
    128         if (mCount == NO_COUNT) {
    129             fillWindow(0);
    130         }
    131         return mCount;
    132     }
    133 
    134     /* 
    135     ** The AbstractWindowClass contains protected methods clearOrCreateWindow() and
    136     ** closeWindow(), which are used by the android.database.sqlite.* version of this
    137     ** class. But, since they are marked with "@hide", the following replacement 
    138     ** versions are required.
    139     */
    140     private void awc_clearOrCreateWindow(String name){
    141       CursorWindow win = getWindow();
    142       if( win==null ){
    143         win = new CursorWindow(name);
    144         setWindow(win);
    145       }else{
    146         win.clear();
    147       }
    148     }
    149     private void awc_closeWindow(){
    150       setWindow(null);
    151     }
    152 
    153     private void fillWindow(int requiredPos) {
    154         awc_clearOrCreateWindow(getDatabase().getPath());
    155 
    156         try {
    157             if (mCount == NO_COUNT) {
    158                 int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
    159                 mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
    160                 mCursorWindowCapacity = mWindow.getNumRows();
    161                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    162                     Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
    163                 }
    164             } else {
    165                 int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
    166                         mCursorWindowCapacity);
    167                 mQuery.fillWindow(mWindow, startPos, requiredPos, false);
    168             }
    169         } catch (RuntimeException ex) {
    170             // Close the cursor window if the query failed and therefore will
    171             // not produce any results.  This helps to avoid accidentally leaking
    172             // the cursor window if the client does not correctly handle exceptions
    173             // and fails to close the cursor.
    174             awc_closeWindow();
    175             throw ex;
    176         }
    177     }
    178 
    179     @Override
    180     public int getColumnIndex(String columnName) {
    181         // Create mColumnNameMap on demand
    182         if (mColumnNameMap == null) {
    183             String[] columns = mColumns;
    184             int columnCount = columns.length;
    185             HashMap<String, Integer> map = new HashMap<String, Integer>(columnCount, 1);
    186             for (int i = 0; i < columnCount; i++) {
    187                 map.put(columns[i], i);
    188             }
    189             mColumnNameMap = map;
    190         }
    191 
    192         // Hack according to bug 903852
    193         final int periodIndex = columnName.lastIndexOf('.');
    194         if (periodIndex != -1) {
    195             Exception e = new Exception();
    196             Log.e(TAG, "requesting column name with table name -- " + columnName, e);
    197             columnName = columnName.substring(periodIndex + 1);
    198         }
    199 
    200         Integer i = mColumnNameMap.get(columnName);
    201         if (i != null) {
    202             return i.intValue();
    203         } else {
    204             return -1;
    205         }
    206     }
    207 
    208     @Override
    209     public String[] getColumnNames() {
    210         return mColumns;
    211     }
    212 
    213     @Override
    214     public void deactivate() {
    215         super.deactivate();
    216         mDriver.cursorDeactivated();
    217     }
    218 
    219     @Override
    220     public void close() {
    221         super.close();
    222         synchronized (this) {
    223             mQuery.close();
    224             mDriver.cursorClosed();
    225         }
    226     }
    227 
    228     @Override
    229     public boolean requery() {
    230         if (isClosed()) {
    231             return false;
    232         }
    233 
    234         synchronized (this) {
    235             if (!mQuery.getDatabase().isOpen()) {
    236                 return false;
    237             }
    238 
    239             if (mWindow != null) {
    240                 mWindow.clear();
    241             }
    242             mPos = -1;
    243             mCount = NO_COUNT;
    244 
    245             mDriver.cursorRequeried(this);
    246         }
    247 
    248         try {
    249             return super.requery();
    250         } catch (IllegalStateException e) {
    251             // for backwards compatibility, just return false
    252             Log.w(TAG, "requery() failed " + e.getMessage(), e);
    253             return false;
    254         }
    255     }
    256 
    257     @Override
    258     public void setWindow(CursorWindow window) {
    259         super.setWindow(window);
    260         mCount = NO_COUNT;
    261     }
    262 
    263     /**
    264      * Changes the selection arguments. The new values take effect after a call to requery().
    265      */
    266     public void setSelectionArguments(String[] selectionArgs) {
    267         mDriver.setBindArguments(selectionArgs);
    268     }
    269 
    270     /**
    271      * Release the native resources, if they haven't been released yet.
    272      */
    273     @Override
    274     protected void finalize() {
    275         try {
    276             // if the cursor hasn't been closed yet, close it first
    277             if (mWindow != null) {
    278                 close();
    279             }
    280         } finally {
    281             super.finalize();
    282         }
    283     }
    284 }