auto import from //depot/cupcake/@135843
This commit is contained in:
@@ -1,636 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.UnsupportedOperationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* This is an abstract cursor class that handles a lot of the common code
|
||||
* that all cursors need to deal with and is provided for convenience reasons.
|
||||
*/
|
||||
public abstract class AbstractCursor implements CrossProcessCursor {
|
||||
private static final String TAG = "Cursor";
|
||||
|
||||
DataSetObservable mDataSetObservable = new DataSetObservable();
|
||||
ContentObservable mContentObservable = new ContentObservable();
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
/* These need to be implemented by subclasses */
|
||||
abstract public int getCount();
|
||||
|
||||
abstract public String[] getColumnNames();
|
||||
|
||||
abstract public String getString(int column);
|
||||
abstract public short getShort(int column);
|
||||
abstract public int getInt(int column);
|
||||
abstract public long getLong(int column);
|
||||
abstract public float getFloat(int column);
|
||||
abstract public double getDouble(int column);
|
||||
abstract public boolean isNull(int column);
|
||||
|
||||
// TODO implement getBlob in all cursor types
|
||||
public byte[] getBlob(int column) {
|
||||
throw new UnsupportedOperationException("getBlob is not supported");
|
||||
}
|
||||
/* -------------------------------------------------------- */
|
||||
/* Methods that may optionally be implemented by subclasses */
|
||||
|
||||
/**
|
||||
* returns a pre-filled window, return NULL if no such window
|
||||
*/
|
||||
public CursorWindow getWindow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getColumnCount() {
|
||||
return getColumnNames().length;
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void deactivateInternal() {
|
||||
if (mSelfObserver != null) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
mSelfObserverRegistered = false;
|
||||
}
|
||||
mDataSetObservable.notifyInvalidated();
|
||||
}
|
||||
|
||||
public boolean requery() {
|
||||
if (mSelfObserver != null && mSelfObserverRegistered == false) {
|
||||
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
|
||||
mSelfObserverRegistered = true;
|
||||
}
|
||||
mDataSetObservable.notifyChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mClosed = true;
|
||||
mContentObservable.unregisterAll();
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates(Map<? extends Long,? extends Map<String,Object>> values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean deleteRow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called every time the cursor is successfully scrolled
|
||||
* to a new position, giving the subclass a chance to update any state it
|
||||
* may have. If it returns false the move function will also do so and the
|
||||
* cursor will scroll to the beforeFirst position.
|
||||
*
|
||||
* @param oldPosition the position that we're moving from
|
||||
* @param newPosition the position that we're moving to
|
||||
* @return true if the move is successful, false otherwise
|
||||
*/
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
||||
// Default implementation, uses getString
|
||||
String result = getString(columnIndex);
|
||||
if (result != null) {
|
||||
char[] data = buffer.data;
|
||||
if (data == null || data.length < result.length()) {
|
||||
buffer.data = result.toCharArray();
|
||||
} else {
|
||||
result.getChars(0, result.length(), data, 0);
|
||||
}
|
||||
buffer.sizeCopied = result.length();
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
/* Implementation */
|
||||
public AbstractCursor() {
|
||||
mPos = -1;
|
||||
mRowIdColumnIndex = -1;
|
||||
mCurrentRowID = null;
|
||||
mUpdatedRows = new HashMap<Long, Map<String, Object>>();
|
||||
}
|
||||
|
||||
public final int getPosition() {
|
||||
return mPos;
|
||||
}
|
||||
|
||||
public final boolean moveToPosition(int position) {
|
||||
// Make sure position isn't past the end of the cursor
|
||||
final int count = getCount();
|
||||
if (position >= count) {
|
||||
mPos = count;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure position isn't before the beginning of the cursor
|
||||
if (position < 0) {
|
||||
mPos = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for no-op moves, and skip the rest of the work for them
|
||||
if (position == mPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean result = onMove(mPos, position);
|
||||
if (result == false) {
|
||||
mPos = -1;
|
||||
} else {
|
||||
mPos = position;
|
||||
if (mRowIdColumnIndex != -1) {
|
||||
mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from cursor to CursorWindow
|
||||
* @param position start position of data
|
||||
* @param window
|
||||
*/
|
||||
public void fillWindow(int position, CursorWindow window) {
|
||||
if (position < 0 || position > getCount()) {
|
||||
return;
|
||||
}
|
||||
window.acquireReference();
|
||||
try {
|
||||
int oldpos = mPos;
|
||||
mPos = position - 1;
|
||||
window.clear();
|
||||
window.setStartPosition(position);
|
||||
int columnNum = getColumnCount();
|
||||
window.setNumColumns(columnNum);
|
||||
while (moveToNext() && window.allocRow()) {
|
||||
for (int i = 0; i < columnNum; i++) {
|
||||
String field = getString(i);
|
||||
if (field != null) {
|
||||
if (!window.putString(field, mPos, i)) {
|
||||
window.freeLastRow();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!window.putNull(mPos, i)) {
|
||||
window.freeLastRow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mPos = oldpos;
|
||||
} catch (IllegalStateException e){
|
||||
// simply ignore it
|
||||
} finally {
|
||||
window.releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean move(int offset) {
|
||||
return moveToPosition(mPos + offset);
|
||||
}
|
||||
|
||||
public final boolean moveToFirst() {
|
||||
return moveToPosition(0);
|
||||
}
|
||||
|
||||
public final boolean moveToLast() {
|
||||
return moveToPosition(getCount() - 1);
|
||||
}
|
||||
|
||||
public final boolean moveToNext() {
|
||||
return moveToPosition(mPos + 1);
|
||||
}
|
||||
|
||||
public final boolean moveToPrevious() {
|
||||
return moveToPosition(mPos - 1);
|
||||
}
|
||||
|
||||
public final boolean isFirst() {
|
||||
return mPos == 0 && getCount() != 0;
|
||||
}
|
||||
|
||||
public final boolean isLast() {
|
||||
int cnt = getCount();
|
||||
return mPos == (cnt - 1) && cnt != 0;
|
||||
}
|
||||
|
||||
public final boolean isBeforeFirst() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return mPos == -1;
|
||||
}
|
||||
|
||||
public final boolean isAfterLast() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return mPos == getCount();
|
||||
}
|
||||
|
||||
public int getColumnIndex(String columnName) {
|
||||
// Hack according to bug 903852
|
||||
final int periodIndex = columnName.lastIndexOf('.');
|
||||
if (periodIndex != -1) {
|
||||
Exception e = new Exception();
|
||||
Log.e(TAG, "requesting column name with table name -- " + columnName, e);
|
||||
columnName = columnName.substring(periodIndex + 1);
|
||||
}
|
||||
|
||||
String columnNames[] = getColumnNames();
|
||||
int length = columnNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (columnNames[i].equalsIgnoreCase(columnName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.LOGV) {
|
||||
if (getCount() > 0) {
|
||||
Log.w("AbstractCursor", "Unknown column " + columnName);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getColumnIndexOrThrow(String columnName) {
|
||||
final int index = getColumnIndex(columnName);
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("column '" + columnName + "' does not exist");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public String getColumnName(int columnIndex) {
|
||||
return getColumnNames()[columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateBlob(int columnIndex, byte[] value) {
|
||||
return update(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateString(int columnIndex, String value) {
|
||||
return update(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateShort(int columnIndex, short value) {
|
||||
return update(columnIndex, Short.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateInt(int columnIndex, int value) {
|
||||
return update(columnIndex, Integer.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateLong(int columnIndex, long value) {
|
||||
return update(columnIndex, Long.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateFloat(int columnIndex, float value) {
|
||||
return update(columnIndex, Float.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateDouble(int columnIndex, double value) {
|
||||
return update(columnIndex, Double.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateToNull(int columnIndex) {
|
||||
return update(columnIndex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean update(int columnIndex, Object obj) {
|
||||
if (!supportsUpdates()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Long.valueOf() returns null sometimes!
|
||||
// Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
|
||||
Long rowid = new Long(getLong(mRowIdColumnIndex));
|
||||
if (rowid == null) {
|
||||
throw new IllegalStateException("null rowid. mRowIdColumnIndex = " + mRowIdColumnIndex);
|
||||
}
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
Map<String, Object> row = mUpdatedRows.get(rowid);
|
||||
if (row == null) {
|
||||
row = new HashMap<String, Object>();
|
||||
mUpdatedRows.put(rowid, row);
|
||||
}
|
||||
row.put(getColumnNames()[columnIndex], obj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if there are pending updates that have not yet been committed.
|
||||
*
|
||||
* @return <code>true</code> if there are pending updates that have not yet been committed.
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean hasUpdates() {
|
||||
synchronized(mUpdatedRows) {
|
||||
return mUpdatedRows.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public void abortUpdates() {
|
||||
synchronized(mUpdatedRows) {
|
||||
mUpdatedRows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates() {
|
||||
return commitUpdates(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean supportsUpdates() {
|
||||
return mRowIdColumnIndex != -1;
|
||||
}
|
||||
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
mContentObservable.registerObserver(observer);
|
||||
}
|
||||
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
// cursor will unregister all observers when it close
|
||||
if (!mClosed) {
|
||||
mContentObservable.unregisterObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide pending API council approval
|
||||
*/
|
||||
protected void notifyDataSetChange() {
|
||||
mDataSetObservable.notifyChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide pending API council approval
|
||||
*/
|
||||
protected DataSetObservable getDataSetObservable() {
|
||||
return mDataSetObservable;
|
||||
|
||||
}
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.registerObserver(observer);
|
||||
|
||||
}
|
||||
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.unregisterObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must call this method when they finish committing updates to notify all
|
||||
* observers.
|
||||
*
|
||||
* @param selfChange
|
||||
*/
|
||||
protected void onChange(boolean selfChange) {
|
||||
synchronized (mSelfObserverLock) {
|
||||
mContentObservable.dispatchChange(selfChange);
|
||||
if (mNotifyUri != null && selfChange) {
|
||||
mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a content URI to watch for changes.
|
||||
*
|
||||
* @param cr The content resolver from the caller's context.
|
||||
* @param notifyUri The URI to watch for changes. This can be a
|
||||
* specific row URI, or a base URI for a whole class of content.
|
||||
*/
|
||||
public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
|
||||
synchronized (mSelfObserverLock) {
|
||||
mNotifyUri = notifyUri;
|
||||
mContentResolver = cr;
|
||||
if (mSelfObserver != null) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
}
|
||||
mSelfObserver = new SelfContentObserver(this);
|
||||
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
|
||||
mSelfObserverRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Bundle getExtras() {
|
||||
return Bundle.EMPTY;
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) {
|
||||
return Bundle.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns true if the field has been updated and is
|
||||
* used in conjunction with {@link #getUpdatedField} to allow subclasses to
|
||||
* support reading uncommitted updates. NOTE: This function and
|
||||
* {@link #getUpdatedField} should be called together inside of a
|
||||
* block synchronized on mUpdatedRows.
|
||||
*
|
||||
* @param columnIndex the column index of the field to check
|
||||
* @return true if the field has been updated, false otherwise
|
||||
*/
|
||||
protected boolean isFieldUpdated(int columnIndex) {
|
||||
if (mRowIdColumnIndex != -1 && mUpdatedRows.size() > 0) {
|
||||
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
|
||||
if (updates != null && updates.containsKey(getColumnNames()[columnIndex])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the uncommitted updated value for the field
|
||||
* at columnIndex. NOTE: This function and {@link #isFieldUpdated} should
|
||||
* be called together inside of a block synchronized on mUpdatedRows.
|
||||
*
|
||||
* @param columnIndex the column index of the field to retrieve
|
||||
* @return the updated value
|
||||
*/
|
||||
protected Object getUpdatedField(int columnIndex) {
|
||||
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
|
||||
return updates.get(getColumnNames()[columnIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function throws CursorIndexOutOfBoundsException if
|
||||
* the cursor position is out of bounds. Subclass implementations of
|
||||
* the get functions should call this before attempting
|
||||
* to retrieve data.
|
||||
*
|
||||
* @throws CursorIndexOutOfBoundsException
|
||||
*/
|
||||
protected void checkPosition() {
|
||||
if (-1 == mPos || getCount() == mPos) {
|
||||
throw new CursorIndexOutOfBoundsException(mPos, getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (mSelfObserver != null && mSelfObserverRegistered == true) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursors use this class to track changes others make to their URI.
|
||||
*/
|
||||
protected static class SelfContentObserver extends ContentObserver {
|
||||
WeakReference<AbstractCursor> mCursor;
|
||||
|
||||
public SelfContentObserver(AbstractCursor cursor) {
|
||||
super(null);
|
||||
mCursor = new WeakReference<AbstractCursor>(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deliverSelfNotifications() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
AbstractCursor cursor = mCursor.get();
|
||||
if (cursor != null) {
|
||||
cursor.onChange(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This HashMap contains a mapping from Long rowIDs to another Map
|
||||
* that maps from String column names to new values. A NULL value means to
|
||||
* remove an existing value, and all numeric values are in their class
|
||||
* forms, i.e. Integer, Long, Float, etc.
|
||||
*/
|
||||
protected HashMap<Long, Map<String, Object>> mUpdatedRows;
|
||||
|
||||
/**
|
||||
* This must be set to the index of the row ID column by any
|
||||
* subclass that wishes to support updates.
|
||||
*/
|
||||
protected int mRowIdColumnIndex;
|
||||
|
||||
protected int mPos;
|
||||
protected Long mCurrentRowID;
|
||||
protected ContentResolver mContentResolver;
|
||||
protected boolean mClosed = false;
|
||||
private Uri mNotifyUri;
|
||||
private ContentObserver mSelfObserver;
|
||||
final private Object mSelfObserverLock = new Object();
|
||||
private boolean mSelfObserverRegistered;
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
/**
|
||||
* A base class for Cursors that store their data in {@link CursorWindow}s.
|
||||
*/
|
||||
public abstract class AbstractWindowedCursor extends AbstractCursor
|
||||
{
|
||||
@Override
|
||||
public byte[] getBlob(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return (byte[])getUpdatedField(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getBlob(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return (String)getUpdatedField(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getString(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
super.copyStringToBuffer(columnIndex, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
mWindow.copyStringToBuffer(mPos, columnIndex, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.shortValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getShort(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getInt(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getLong(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getFloat(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getDouble(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return getUpdatedField(columnIndex) == null;
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isNull(mPos, columnIndex);
|
||||
}
|
||||
|
||||
public boolean isBlob(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Object object = getUpdatedField(columnIndex);
|
||||
return object == null || object instanceof byte[];
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isBlob(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkPosition()
|
||||
{
|
||||
super.checkPosition();
|
||||
|
||||
if (mWindow == null) {
|
||||
throw new StaleDataException("Access closed cursor");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorWindow getWindow() {
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new cursor window to cursor, usually set a remote cursor window
|
||||
* @param window cursor window
|
||||
*/
|
||||
public void setWindow(CursorWindow window) {
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
}
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
public boolean hasWindow() {
|
||||
return mWindow != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This needs be updated in {@link #onMove} by subclasses, and
|
||||
* needs to be set to NULL when the contents of the cursor change.
|
||||
*/
|
||||
protected CursorWindow mWindow;
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Native implementation of the bulk cursor. This is only for use in implementing
|
||||
* IPC, application code should use the Cursor interface.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public abstract class BulkCursorNative extends Binder implements IBulkCursor
|
||||
{
|
||||
public BulkCursorNative()
|
||||
{
|
||||
attachInterface(this, descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a Binder object into a content resolver interface, generating
|
||||
* a proxy if needed.
|
||||
*/
|
||||
static public IBulkCursor asInterface(IBinder obj)
|
||||
{
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
IBulkCursor in = (IBulkCursor)obj.queryLocalInterface(descriptor);
|
||||
if (in != null) {
|
||||
return in;
|
||||
}
|
||||
|
||||
return new BulkCursorProxy(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||
throws RemoteException {
|
||||
try {
|
||||
switch (code) {
|
||||
case GET_CURSOR_WINDOW_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int startPos = data.readInt();
|
||||
CursorWindow window = getWindow(startPos);
|
||||
if (window == null) {
|
||||
reply.writeInt(0);
|
||||
return true;
|
||||
}
|
||||
reply.writeNoException();
|
||||
reply.writeInt(1);
|
||||
window.writeToParcel(reply, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
case COUNT_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int count = count();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(count);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GET_COLUMN_NAMES_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
String[] columnNames = getColumnNames();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(columnNames.length);
|
||||
int length = columnNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
reply.writeString(columnNames[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case DEACTIVATE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
deactivate();
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case CLOSE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
close();
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case REQUERY_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
IContentObserver observer =
|
||||
IContentObserver.Stub.asInterface(data.readStrongBinder());
|
||||
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
|
||||
int count = requery(observer, window);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(count);
|
||||
reply.writeBundle(getExtras());
|
||||
return true;
|
||||
}
|
||||
|
||||
case UPDATE_ROWS_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
// TODO - what ClassLoader should be passed to readHashMap?
|
||||
// TODO - switch to Bundle
|
||||
HashMap<Long, Map<String, Object>> values = data.readHashMap(null);
|
||||
boolean result = updateRows(values);
|
||||
reply.writeNoException();
|
||||
reply.writeInt((result == true ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case DELETE_ROW_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int position = data.readInt();
|
||||
boolean result = deleteRow(position);
|
||||
reply.writeNoException();
|
||||
reply.writeInt((result == true ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case ON_MOVE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int position = data.readInt();
|
||||
onMove(position);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case WANTS_ON_MOVE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
boolean result = getWantsAllOnMoveCalls();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(result ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GET_EXTRAS_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
Bundle extras = getExtras();
|
||||
reply.writeNoException();
|
||||
reply.writeBundle(extras);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RESPOND_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
Bundle extras = data.readBundle();
|
||||
Bundle returnExtras = respond(extras);
|
||||
reply.writeNoException();
|
||||
reply.writeBundle(returnExtras);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DatabaseUtils.writeExceptionToParcel(reply, e);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
public IBinder asBinder()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class BulkCursorProxy implements IBulkCursor {
|
||||
private IBinder mRemote;
|
||||
private Bundle mExtras;
|
||||
|
||||
public BulkCursorProxy(IBinder remote)
|
||||
{
|
||||
mRemote = remote;
|
||||
mExtras = null;
|
||||
}
|
||||
|
||||
public IBinder asBinder()
|
||||
{
|
||||
return mRemote;
|
||||
}
|
||||
|
||||
public CursorWindow getWindow(int startPos) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(startPos);
|
||||
|
||||
mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
CursorWindow window = null;
|
||||
if (reply.readInt() == 1) {
|
||||
window = CursorWindow.newFromParcel(reply);
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public void onMove(int position) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(position);
|
||||
|
||||
mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public int count() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int count;
|
||||
if (result == false) {
|
||||
count = -1;
|
||||
} else {
|
||||
count = reply.readInt();
|
||||
}
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return count;
|
||||
}
|
||||
|
||||
public String[] getColumnNames() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
String[] columnNames = null;
|
||||
int numColumns = reply.readInt();
|
||||
columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
columnNames[i] = reply.readString();
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public void deactivate() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(DEACTIVATE_TRANSACTION, data, reply, 0);
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public void close() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(CLOSE_TRANSACTION, data, reply, 0);
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeStrongInterface(observer);
|
||||
window.writeToParcel(data, 0);
|
||||
|
||||
boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int count;
|
||||
if (!result) {
|
||||
count = -1;
|
||||
} else {
|
||||
count = reply.readInt();
|
||||
mExtras = reply.readBundle();
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean updateRows(Map values) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeMap(values);
|
||||
|
||||
mRemote.transact(UPDATE_ROWS_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
boolean result = (reply.readInt() == 1 ? true : false);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean deleteRow(int position) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(position);
|
||||
|
||||
mRemote.transact(DELETE_ROW_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
boolean result = (reply.readInt() == 1 ? true : false);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int result = reply.readInt();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
public Bundle getExtras() throws RemoteException {
|
||||
if (mExtras == null) {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(GET_EXTRAS_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
mExtras = reply.readBundle();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeBundle(extras);
|
||||
|
||||
mRemote.transact(RESPOND_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
Bundle returnExtras = reply.readBundle();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return returnExtras;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
|
||||
* process.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
private static final String TAG = "BulkCursor";
|
||||
|
||||
private SelfContentObserver mObserverBridge;
|
||||
private IBulkCursor mBulkCursor;
|
||||
private int mCount;
|
||||
private String[] mColumns;
|
||||
private boolean mWantsAllOnMoveCalls;
|
||||
|
||||
public void set(IBulkCursor bulkCursor) {
|
||||
mBulkCursor = bulkCursor;
|
||||
|
||||
try {
|
||||
mCount = mBulkCursor.count();
|
||||
mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
|
||||
|
||||
// Search for the rowID column index and set it for our parent
|
||||
mColumns = mBulkCursor.getColumnNames();
|
||||
int length = mColumns.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (mColumns[i].equals("_id")) {
|
||||
mRowIdColumnIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Setup failed because the remote process is dead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a SelfDataChangeOberserver that can be sent to a remote
|
||||
* process to receive change notifications over IPC.
|
||||
*
|
||||
* @return A SelfContentObserver hooked up to this Cursor
|
||||
*/
|
||||
public synchronized IContentObserver getObserver() {
|
||||
if (mObserverBridge == null) {
|
||||
mObserverBridge = new SelfContentObserver(this);
|
||||
}
|
||||
return mObserverBridge.getContentObserver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
try {
|
||||
// Make sure we have the proper window
|
||||
if (mWindow != null) {
|
||||
if (newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
} else if (mWantsAllOnMoveCalls) {
|
||||
mBulkCursor.onMove(newPosition);
|
||||
}
|
||||
} else {
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// We tried to get a window and failed
|
||||
Log.e(TAG, "Unable to get window because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Couldn't obtain a window, something is wrong
|
||||
if (mWindow == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
// This will call onInvalidated(), so make sure to do it before calling release,
|
||||
// which is what actually makes the data set invalid.
|
||||
super.deactivate();
|
||||
|
||||
try {
|
||||
mBulkCursor.deactivate();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when deactivating");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
try {
|
||||
mBulkCursor.close();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when closing");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery() {
|
||||
try {
|
||||
int oldCount = mCount;
|
||||
//TODO get the window from a pool somewhere to avoid creating the memory dealer
|
||||
mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
|
||||
false /* the window will be accessed across processes */));
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
mWindow = null;
|
||||
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean deleteRow() {
|
||||
try {
|
||||
boolean result = mBulkCursor.deleteRow(mPos);
|
||||
if (result != false) {
|
||||
// The window contains the old value, discard it
|
||||
mWindow = null;
|
||||
|
||||
// Fix up the position
|
||||
mCount = mBulkCursor.count();
|
||||
if (mPos < mCount) {
|
||||
int oldPos = mPos;
|
||||
mPos = -1;
|
||||
moveToPosition(oldPos);
|
||||
} else {
|
||||
mPos = mCount;
|
||||
}
|
||||
|
||||
// Send the change notification
|
||||
onChange(true);
|
||||
}
|
||||
return result;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to delete row because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean commitUpdates(Map<? extends Long,
|
||||
? extends Map<String,Object>> additionalValues) {
|
||||
if (!supportsUpdates()) {
|
||||
Log.e(TAG, "commitUpdates not supported on this cursor, did you include the _id column?");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (additionalValues != null) {
|
||||
mUpdatedRows.putAll(additionalValues);
|
||||
}
|
||||
|
||||
if (mUpdatedRows.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean result = mBulkCursor.updateRows(mUpdatedRows);
|
||||
|
||||
if (result == true) {
|
||||
mUpdatedRows.clear();
|
||||
|
||||
// Send the change notification
|
||||
onChange(true);
|
||||
}
|
||||
return result;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to commit updates because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getExtras() {
|
||||
try {
|
||||
return mBulkCursor.getExtras();
|
||||
} catch (RemoteException e) {
|
||||
// This should never happen because the system kills processes that are using remote
|
||||
// cursors when the provider process is killed.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle respond(Bundle extras) {
|
||||
try {
|
||||
return mBulkCursor.respond(extras);
|
||||
} catch (RemoteException e) {
|
||||
// This should never happen because the system kills processes that are using remote
|
||||
// cursors when the provider process is killed.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database;
|
||||
|
||||
/**
|
||||
* This is used for {@link Cursor#copyStringToBuffer}
|
||||
*/
|
||||
public final class CharArrayBuffer {
|
||||
public CharArrayBuffer(int size) {
|
||||
data = new char[size];
|
||||
}
|
||||
|
||||
public CharArrayBuffer(char[] buf) {
|
||||
data = buf;
|
||||
}
|
||||
|
||||
public char[] data; // In and out parameter
|
||||
public int sizeCopied; // Out parameter
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
/**
|
||||
* A specialization of Observable for ContentObserver that provides methods for
|
||||
* invoking the various callback methods of ContentObserver.
|
||||
*/
|
||||
public class ContentObservable extends Observable<ContentObserver> {
|
||||
|
||||
@Override
|
||||
public void registerObserver(ContentObserver observer) {
|
||||
super.registerObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* invokes dispatchUpdate on each observer, unless the observer doesn't want
|
||||
* self-notifications and the update is from a self-notification
|
||||
* @param selfChange
|
||||
*/
|
||||
public void dispatchChange(boolean selfChange) {
|
||||
synchronized(mObservers) {
|
||||
for (ContentObserver observer : mObservers) {
|
||||
if (!selfChange || observer.deliverSelfNotifications()) {
|
||||
observer.dispatchChange(selfChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* invokes onChange on each observer
|
||||
* @param selfChange
|
||||
*/
|
||||
public void notifyChange(boolean selfChange) {
|
||||
synchronized(mObservers) {
|
||||
for (ContentObserver observer : mObservers) {
|
||||
observer.onChange(selfChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
/**
|
||||
* Receives call backs for changes to content. Must be implemented by objects which are added
|
||||
* to a {@link ContentObservable}.
|
||||
*/
|
||||
public abstract class ContentObserver {
|
||||
|
||||
private Transport mTransport;
|
||||
|
||||
// Protects mTransport
|
||||
private Object lock = new Object();
|
||||
|
||||
/* package */ Handler mHandler;
|
||||
|
||||
private final class NotificationRunnable implements Runnable {
|
||||
|
||||
private boolean mSelf;
|
||||
|
||||
public NotificationRunnable(boolean self) {
|
||||
mSelf = self;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
ContentObserver.this.onChange(mSelf);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Transport extends IContentObserver.Stub {
|
||||
ContentObserver mContentObserver;
|
||||
|
||||
public Transport(ContentObserver contentObserver) {
|
||||
mContentObserver = contentObserver;
|
||||
}
|
||||
|
||||
public boolean deliverSelfNotifications() {
|
||||
ContentObserver contentObserver = mContentObserver;
|
||||
if (contentObserver != null) {
|
||||
return contentObserver.deliverSelfNotifications();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onChange(boolean selfChange) {
|
||||
ContentObserver contentObserver = mContentObserver;
|
||||
if (contentObserver != null) {
|
||||
contentObserver.dispatchChange(selfChange);
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseContentObserver() {
|
||||
mContentObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onChange() will happen on the provider Handler.
|
||||
*
|
||||
* @param handler The handler to run {@link #onChange} on.
|
||||
*/
|
||||
public ContentObserver(Handler handler) {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets access to the binder transport object. Not for public consumption.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public IContentObserver getContentObserver() {
|
||||
synchronized(lock) {
|
||||
if (mTransport == null) {
|
||||
mTransport = new Transport(this);
|
||||
}
|
||||
return mTransport;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets access to the binder transport object, and unlinks the transport object
|
||||
* from the ContentObserver. Not for public consumption.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public IContentObserver releaseContentObserver() {
|
||||
synchronized(lock) {
|
||||
Transport oldTransport = mTransport;
|
||||
if (oldTransport != null) {
|
||||
oldTransport.releaseContentObserver();
|
||||
mTransport = null;
|
||||
}
|
||||
return oldTransport;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this observer is interested in notifications for changes
|
||||
* made through the cursor the observer is registered with.
|
||||
*/
|
||||
public boolean deliverSelfNotifications() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a change occurs to the cursor that
|
||||
* is being observed.
|
||||
*
|
||||
* @param selfChange true if the update was caused by a call to <code>commit</code> on the
|
||||
* cursor that is being observed.
|
||||
*/
|
||||
public void onChange(boolean selfChange) {}
|
||||
|
||||
public final void dispatchChange(boolean selfChange) {
|
||||
if (mHandler == null) {
|
||||
onChange(selfChange);
|
||||
} else {
|
||||
mHandler.post(new NotificationRunnable(selfChange));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database;
|
||||
|
||||
public interface CrossProcessCursor extends Cursor{
|
||||
/**
|
||||
* returns a pre-filled window, return NULL if no such window
|
||||
*/
|
||||
CursorWindow getWindow();
|
||||
|
||||
/**
|
||||
* copies cursor data into the window start at pos
|
||||
*/
|
||||
void fillWindow(int pos, CursorWindow winow);
|
||||
|
||||
/**
|
||||
* This function is called every time the cursor is successfully scrolled
|
||||
* to a new position, giving the subclass a chance to update any state it
|
||||
* may have. If it returns false the move function will also do so and the
|
||||
* cursor will scroll to the beforeFirst position.
|
||||
*
|
||||
* @param oldPosition the position that we're moving from
|
||||
* @param newPosition the position that we're moving to
|
||||
* @return true if the move is successful, false otherwise
|
||||
*/
|
||||
boolean onMove(int oldPosition, int newPosition);
|
||||
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface provides random read-write access to the result set returned
|
||||
* by a database query.
|
||||
*/
|
||||
public interface Cursor {
|
||||
/**
|
||||
* Returns the numbers of rows in the cursor.
|
||||
*
|
||||
* @return the number of rows in the cursor.
|
||||
*/
|
||||
int getCount();
|
||||
|
||||
/**
|
||||
* Returns the current position of the cursor in the row set.
|
||||
* The value is zero-based. When the row set is first returned the cursor
|
||||
* will be at positon -1, which is before the first row. After the
|
||||
* last row is returned another call to next() will leave the cursor past
|
||||
* the last entry, at a position of count().
|
||||
*
|
||||
* @return the current cursor position.
|
||||
*/
|
||||
int getPosition();
|
||||
|
||||
/**
|
||||
* Move the cursor by a relative amount, forward or backward, from the
|
||||
* current position. Positive offsets move forwards, negative offsets move
|
||||
* backwards. If the final position is outside of the bounds of the result
|
||||
* set then the resultant position will be pinned to -1 or count() depending
|
||||
* on whether the value is off the front or end of the set, respectively.
|
||||
*
|
||||
* <p>This method will return true if the requested destination was
|
||||
* reachable, otherwise, it returns false. For example, if the cursor is at
|
||||
* currently on the second entry in the result set and move(-5) is called,
|
||||
* the position will be pinned at -1, and false will be returned.
|
||||
*
|
||||
* @param offset the offset to be applied from the current position.
|
||||
* @return whether the requested move fully succeeded.
|
||||
*/
|
||||
boolean move(int offset);
|
||||
|
||||
/**
|
||||
* Move the cursor to an absolute position. The valid
|
||||
* range of values is -1 <= position <= count.
|
||||
*
|
||||
* <p>This method will return true if the request destination was reachable,
|
||||
* otherwise, it returns false.
|
||||
*
|
||||
* @param position the zero-based position to move to.
|
||||
* @return whether the requested move fully succeeded.
|
||||
*/
|
||||
boolean moveToPosition(int position);
|
||||
|
||||
/**
|
||||
* Move the cursor to the first row.
|
||||
*
|
||||
* <p>This method will return false if the cursor is empty.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
boolean moveToFirst();
|
||||
|
||||
/**
|
||||
* Move the cursor to the last row.
|
||||
*
|
||||
* <p>This method will return false if the cursor is empty.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
boolean moveToLast();
|
||||
|
||||
/**
|
||||
* Move the cursor to the next row.
|
||||
*
|
||||
* <p>This method will return false if the cursor is already past the
|
||||
* last entry in the result set.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
boolean moveToNext();
|
||||
|
||||
/**
|
||||
* Move the cursor to the previous row.
|
||||
*
|
||||
* <p>This method will return false if the cursor is already before the
|
||||
* first entry in the result set.
|
||||
*
|
||||
* @return whether the move succeeded.
|
||||
*/
|
||||
boolean moveToPrevious();
|
||||
|
||||
/**
|
||||
* Returns whether the cursor is pointing to the first row.
|
||||
*
|
||||
* @return whether the cursor is pointing at the first entry.
|
||||
*/
|
||||
boolean isFirst();
|
||||
|
||||
/**
|
||||
* Returns whether the cursor is pointing to the last row.
|
||||
*
|
||||
* @return whether the cursor is pointing at the last entry.
|
||||
*/
|
||||
boolean isLast();
|
||||
|
||||
/**
|
||||
* Returns whether the cursor is pointing to the position before the first
|
||||
* row.
|
||||
*
|
||||
* @return whether the cursor is before the first result.
|
||||
*/
|
||||
boolean isBeforeFirst();
|
||||
|
||||
/**
|
||||
* Returns whether the cursor is pointing to the position after the last
|
||||
* row.
|
||||
*
|
||||
* @return whether the cursor is after the last result.
|
||||
*/
|
||||
boolean isAfterLast();
|
||||
|
||||
/**
|
||||
* Removes the row at the current cursor position from the underlying data
|
||||
* store. After this method returns the cursor will be pointing to the row
|
||||
* after the row that is deleted. This has the side effect of decrementing
|
||||
* the result of count() by one.
|
||||
* <p>
|
||||
* The query must have the row ID column in its selection, otherwise this
|
||||
* call will fail.
|
||||
*
|
||||
* @hide
|
||||
* @return whether the record was successfully deleted.
|
||||
* @deprecated use {@link ContentResolver#delete(Uri, String, String[])}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean deleteRow();
|
||||
|
||||
/**
|
||||
* Returns the zero-based index for the given column name, or -1 if the column doesn't exist.
|
||||
* If you expect the column to exist use {@link #getColumnIndexOrThrow(String)} instead, which
|
||||
* will make the error more clear.
|
||||
*
|
||||
* @param columnName the name of the target column.
|
||||
* @return the zero-based column index for the given column name, or -1 if
|
||||
* the column name does not exist.
|
||||
* @see #getColumnIndexOrThrow(String)
|
||||
*/
|
||||
int getColumnIndex(String columnName);
|
||||
|
||||
/**
|
||||
* Returns the zero-based index for the given column name, or throws
|
||||
* {@link IllegalArgumentException} if the column doesn't exist. If you're not sure if
|
||||
* a column will exist or not use {@link #getColumnIndex(String)} and check for -1, which
|
||||
* is more efficient than catching the exceptions.
|
||||
*
|
||||
* @param columnName the name of the target column.
|
||||
* @return the zero-based column index for the given column name
|
||||
* @see #getColumnIndex(String)
|
||||
* @throws IllegalArgumentException if the column does not exist
|
||||
*/
|
||||
int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Returns the column name at the given zero-based column index.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the column name for the given column index.
|
||||
*/
|
||||
String getColumnName(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns a string array holding the names of all of the columns in the
|
||||
* result set in the order in which they were listed in the result.
|
||||
*
|
||||
* @return the names of the columns returned in this query.
|
||||
*/
|
||||
String[] getColumnNames();
|
||||
|
||||
/**
|
||||
* Return total number of columns
|
||||
* @return number of columns
|
||||
*/
|
||||
int getColumnCount();
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a byte array.
|
||||
*
|
||||
* <p>If the native content of that column is not blob exception may throw
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a byte array.
|
||||
*/
|
||||
byte[] getBlob(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a String.
|
||||
*
|
||||
* <p>If the native content of that column is not text the result will be
|
||||
* the result of passing the column value to String.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a String.
|
||||
*/
|
||||
String getString(int columnIndex);
|
||||
|
||||
/**
|
||||
* Retrieves the requested column text and stores it in the buffer provided.
|
||||
* If the buffer size is not sufficient, a new char buffer will be allocated
|
||||
* and assigned to CharArrayBuffer.data
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* if the target column is null, return buffer
|
||||
* @param buffer the buffer to copy the text into.
|
||||
*/
|
||||
void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a short.
|
||||
*
|
||||
* <p>If the native content of that column is not numeric the result will be
|
||||
* the result of passing the column value to Short.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a short.
|
||||
*/
|
||||
short getShort(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as an int.
|
||||
*
|
||||
* <p>If the native content of that column is not numeric the result will be
|
||||
* the result of passing the column value to Integer.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as an int.
|
||||
*/
|
||||
int getInt(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a long.
|
||||
*
|
||||
* <p>If the native content of that column is not numeric the result will be
|
||||
* the result of passing the column value to Long.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a long.
|
||||
*/
|
||||
long getLong(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a float.
|
||||
*
|
||||
* <p>If the native content of that column is not numeric the result will be
|
||||
* the result of passing the column value to Float.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a float.
|
||||
*/
|
||||
float getFloat(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the value of the requested column as a double.
|
||||
*
|
||||
* <p>If the native content of that column is not numeric the result will be
|
||||
* the result of passing the column value to Double.valueOf(x).
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return the value of that column as a double.
|
||||
*/
|
||||
double getDouble(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the value in the indicated column is null.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return whether the column value is null.
|
||||
*/
|
||||
boolean isNull(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the cursor supports updates.
|
||||
*
|
||||
* @return whether the cursor supports updates.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean supportsUpdates();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if there are pending updates that have not yet been committed.
|
||||
*
|
||||
* @return <code>true</code> if there are pending updates that have not yet been committed.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean hasUpdates();
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateBlob(int columnIndex, byte[] value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateString(int columnIndex, String value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateShort(int columnIndex, short value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateInt(int columnIndex, int value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateLong(int columnIndex, long value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateFloat(int columnIndex, float value);
|
||||
|
||||
/**
|
||||
* Updates the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @param value the new value.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateDouble(int columnIndex, double value);
|
||||
|
||||
/**
|
||||
* Removes the value for the given column in the row the cursor is
|
||||
* currently pointing at. Updates are not committed to the backing store
|
||||
* until {@link #commitUpdates()} is called.
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean updateToNull(int columnIndex);
|
||||
|
||||
/**
|
||||
* Atomically commits all updates to the backing store. After completion,
|
||||
* this method leaves the data in an inconsistent state and you should call
|
||||
* {@link #requery} before reading data from the cursor again.
|
||||
*
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean commitUpdates();
|
||||
|
||||
/**
|
||||
* Atomically commits all updates to the backing store, as well as the
|
||||
* updates included in values. After completion,
|
||||
* this method leaves the data in an inconsistent state and you should call
|
||||
* {@link #requery} before reading data from the cursor again.
|
||||
*
|
||||
* @param values A map from row IDs to Maps associating column names with
|
||||
* updated values. A null value indicates the field should be
|
||||
removed.
|
||||
* @return whether the operation succeeded.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
boolean commitUpdates(Map<? extends Long,
|
||||
? extends Map<String,Object>> values);
|
||||
|
||||
/**
|
||||
* Reverts all updates made to the cursor since the last call to
|
||||
* commitUpdates.
|
||||
* @hide
|
||||
* @deprecated use the {@link ContentResolver} update methods instead of the Cursor
|
||||
* update methods
|
||||
*/
|
||||
@Deprecated
|
||||
void abortUpdates();
|
||||
|
||||
/**
|
||||
* Deactivates the Cursor, making all calls on it fail until {@link #requery} is called.
|
||||
* Inactive Cursors use fewer resources than active Cursors.
|
||||
* Calling {@link #requery} will make the cursor active again.
|
||||
*/
|
||||
void deactivate();
|
||||
|
||||
/**
|
||||
* Performs the query that created the cursor again, refreshing its
|
||||
* contents. This may be done at any time, including after a call to {@link
|
||||
* #deactivate}.
|
||||
*
|
||||
* @return true if the requery succeeded, false if not, in which case the
|
||||
* cursor becomes invalid.
|
||||
*/
|
||||
boolean requery();
|
||||
|
||||
/**
|
||||
* Closes the Cursor, releasing all of its resources and making it completely invalid.
|
||||
* Unlike {@link #deactivate()} a call to {@link #requery()} will not make the Cursor valid
|
||||
* again.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* return true if the cursor is closed
|
||||
* @return true if the cursor is closed.
|
||||
*/
|
||||
boolean isClosed();
|
||||
|
||||
/**
|
||||
* Register an observer that is called when changes happen to the content backing this cursor.
|
||||
* Typically the data set won't change until {@link #requery()} is called.
|
||||
*
|
||||
* @param observer the object that gets notified when the content backing the cursor changes.
|
||||
* @see #unregisterContentObserver(ContentObserver)
|
||||
*/
|
||||
void registerContentObserver(ContentObserver observer);
|
||||
|
||||
/**
|
||||
* Unregister an observer that has previously been registered with this
|
||||
* cursor via {@link #registerContentObserver}.
|
||||
*
|
||||
* @param observer the object to unregister.
|
||||
* @see #registerContentObserver(ContentObserver)
|
||||
*/
|
||||
void unregisterContentObserver(ContentObserver observer);
|
||||
|
||||
/**
|
||||
* Register an observer that is called when changes happen to the contents
|
||||
* of the this cursors data set, for example, when the data set is changed via
|
||||
* {@link #requery()}, {@link #deactivate()}, or {@link #close()}.
|
||||
*
|
||||
* @param observer the object that gets notified when the cursors data set changes.
|
||||
* @see #unregisterDataSetObserver(DataSetObserver)
|
||||
*/
|
||||
void registerDataSetObserver(DataSetObserver observer);
|
||||
|
||||
/**
|
||||
* Unregister an observer that has previously been registered with this
|
||||
* cursor via {@link #registerContentObserver}.
|
||||
*
|
||||
* @param observer the object to unregister.
|
||||
* @see #registerDataSetObserver(DataSetObserver)
|
||||
*/
|
||||
void unregisterDataSetObserver(DataSetObserver observer);
|
||||
|
||||
/**
|
||||
* Register to watch a content URI for changes. This can be the URI of a specific data row (for
|
||||
* example, "content://my_provider_type/23"), or a a generic URI for a content type.
|
||||
*
|
||||
* @param cr The content resolver from the caller's context. The listener attached to
|
||||
* this resolver will be notified.
|
||||
* @param uri The content URI to watch.
|
||||
*/
|
||||
void setNotificationUri(ContentResolver cr, Uri uri);
|
||||
|
||||
/**
|
||||
* onMove() will only be called across processes if this method returns true.
|
||||
* @return whether all cursor movement should result in a call to onMove().
|
||||
*/
|
||||
boolean getWantsAllOnMoveCalls();
|
||||
|
||||
/**
|
||||
* Returns a bundle of extra values. This is an optional way for cursors to provide out-of-band
|
||||
* metadata to their users. One use of this is for reporting on the progress of network requests
|
||||
* that are required to fetch data for the cursor.
|
||||
*
|
||||
* <p>These values may only change when requery is called.
|
||||
* @return cursor-defined values, or Bundle.EMTPY if there are no values. Never null.
|
||||
*/
|
||||
Bundle getExtras();
|
||||
|
||||
/**
|
||||
* This is an out-of-band way for the the user of a cursor to communicate with the cursor. The
|
||||
* structure of each bundle is entirely defined by the cursor.
|
||||
*
|
||||
* <p>One use of this is to tell a cursor that it should retry its network request after it
|
||||
* reported an error.
|
||||
* @param extras extra values, or Bundle.EMTPY. Never null.
|
||||
* @return extra values, or Bundle.EMTPY. Never null.
|
||||
*/
|
||||
Bundle respond(Bundle extras);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
/**
|
||||
* An exception indicating that a cursor is out of bounds.
|
||||
*/
|
||||
public class CursorIndexOutOfBoundsException extends IndexOutOfBoundsException {
|
||||
|
||||
public CursorIndexOutOfBoundsException(int index, int size) {
|
||||
super("Index " + index + " requested, with a size of " + size);
|
||||
}
|
||||
|
||||
public CursorIndexOutOfBoundsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Does a join on two cursors using the specified columns. The cursors must already
|
||||
* be sorted on each of the specified columns in ascending order. This joiner only
|
||||
* supports the case where the tuple of key column values is unique.
|
||||
* <p>
|
||||
* Typical usage:
|
||||
*
|
||||
* <pre>
|
||||
* CursorJoiner joiner = new CursorJoiner(cursorA, keyColumnsofA, cursorB, keyColumnsofB);
|
||||
* for (CursorJointer.Result joinerResult : joiner) {
|
||||
* switch (joinerResult) {
|
||||
* case LEFT:
|
||||
* // handle case where a row in cursorA is unique
|
||||
* break;
|
||||
* case RIGHT:
|
||||
* // handle case where a row in cursorB is unique
|
||||
* break;
|
||||
* case BOTH:
|
||||
* // handle case where a row with the same key is in both cursors
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public final class CursorJoiner
|
||||
implements Iterator<CursorJoiner.Result>, Iterable<CursorJoiner.Result> {
|
||||
private Cursor mCursorLeft;
|
||||
private Cursor mCursorRight;
|
||||
private boolean mCompareResultIsValid;
|
||||
private Result mCompareResult;
|
||||
private int[] mColumnsLeft;
|
||||
private int[] mColumnsRight;
|
||||
private String[] mValues;
|
||||
|
||||
/**
|
||||
* The result of a call to next().
|
||||
*/
|
||||
public enum Result {
|
||||
/** The row currently pointed to by the left cursor is unique */
|
||||
RIGHT,
|
||||
/** The row currently pointed to by the right cursor is unique */
|
||||
LEFT,
|
||||
/** The rows pointed to by both cursors are the same */
|
||||
BOTH
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the CursorJoiner and resets the cursors to the first row. The left and right
|
||||
* column name arrays must have the same number of columns.
|
||||
* @param cursorLeft The left cursor to compare
|
||||
* @param columnNamesLeft The column names to compare from the left cursor
|
||||
* @param cursorRight The right cursor to compare
|
||||
* @param columnNamesRight The column names to compare from the right cursor
|
||||
*/
|
||||
public CursorJoiner(
|
||||
Cursor cursorLeft, String[] columnNamesLeft,
|
||||
Cursor cursorRight, String[] columnNamesRight) {
|
||||
if (columnNamesLeft.length != columnNamesRight.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"you must have the same number of columns on the left and right, "
|
||||
+ columnNamesLeft.length + " != " + columnNamesRight.length);
|
||||
}
|
||||
|
||||
mCursorLeft = cursorLeft;
|
||||
mCursorRight = cursorRight;
|
||||
|
||||
mCursorLeft.moveToFirst();
|
||||
mCursorRight.moveToFirst();
|
||||
|
||||
mCompareResultIsValid = false;
|
||||
|
||||
mColumnsLeft = buildColumnIndiciesArray(cursorLeft, columnNamesLeft);
|
||||
mColumnsRight = buildColumnIndiciesArray(cursorRight, columnNamesRight);
|
||||
|
||||
mValues = new String[mColumnsLeft.length * 2];
|
||||
}
|
||||
|
||||
public Iterator<Result> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the indicies of the each column name and return them in an array.
|
||||
* @param cursor the cursor that contains the columns
|
||||
* @param columnNames the array of names to lookup
|
||||
* @return an array of column indices
|
||||
*/
|
||||
private int[] buildColumnIndiciesArray(Cursor cursor, String[] columnNames) {
|
||||
int[] columns = new int[columnNames.length];
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
columns[i] = cursor.getColumnIndexOrThrow(columnNames[i]);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there are more rows to compare using next().
|
||||
* @return true if there are more rows to compare
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
if (mCompareResultIsValid) {
|
||||
switch (mCompareResult) {
|
||||
case BOTH:
|
||||
return !mCursorLeft.isLast() || !mCursorRight.isLast();
|
||||
|
||||
case LEFT:
|
||||
return !mCursorLeft.isLast() || !mCursorRight.isAfterLast();
|
||||
|
||||
case RIGHT:
|
||||
return !mCursorLeft.isAfterLast() || !mCursorRight.isLast();
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("bad value for mCompareResult, "
|
||||
+ mCompareResult);
|
||||
}
|
||||
} else {
|
||||
return !mCursorLeft.isAfterLast() || !mCursorRight.isAfterLast();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comparison result of the next row from each cursor. If one cursor
|
||||
* has no more rows but the other does then subsequent calls to this will indicate that
|
||||
* the remaining rows are unique.
|
||||
* <p>
|
||||
* The caller must check that hasNext() returns true before calling this.
|
||||
* <p>
|
||||
* Once next() has been called the cursors specified in the result of the call to
|
||||
* next() are guaranteed to point to the row that was indicated. Reading values
|
||||
* from the cursor that was not indicated in the call to next() will result in
|
||||
* undefined behavior.
|
||||
* @return LEFT, if the row pointed to by the left cursor is unique, RIGHT
|
||||
* if the row pointed to by the right cursor is unique, BOTH if the rows in both
|
||||
* cursors are the same.
|
||||
*/
|
||||
public Result next() {
|
||||
if (!hasNext()) {
|
||||
throw new IllegalStateException("you must only call next() when hasNext() is true");
|
||||
}
|
||||
incrementCursors();
|
||||
assert hasNext();
|
||||
|
||||
boolean hasLeft = !mCursorLeft.isAfterLast();
|
||||
boolean hasRight = !mCursorRight.isAfterLast();
|
||||
|
||||
if (hasLeft && hasRight) {
|
||||
populateValues(mValues, mCursorLeft, mColumnsLeft, 0 /* start filling at index 0 */);
|
||||
populateValues(mValues, mCursorRight, mColumnsRight, 1 /* start filling at index 1 */);
|
||||
switch (compareStrings(mValues)) {
|
||||
case -1:
|
||||
mCompareResult = Result.LEFT;
|
||||
break;
|
||||
case 0:
|
||||
mCompareResult = Result.BOTH;
|
||||
break;
|
||||
case 1:
|
||||
mCompareResult = Result.RIGHT;
|
||||
break;
|
||||
}
|
||||
} else if (hasLeft) {
|
||||
mCompareResult = Result.LEFT;
|
||||
} else {
|
||||
assert hasRight;
|
||||
mCompareResult = Result.RIGHT;
|
||||
}
|
||||
mCompareResultIsValid = true;
|
||||
return mCompareResult;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the strings from the cursor that are specifed in the columnIndicies
|
||||
* array and saves them in values beginning at startingIndex, skipping a slot
|
||||
* for each value. If columnIndicies has length 3 and startingIndex is 1, the
|
||||
* values will be stored in slots 1, 3, and 5.
|
||||
* @param values the String[] to populate
|
||||
* @param cursor the cursor from which to read
|
||||
* @param columnIndicies the indicies of the values to read from the cursor
|
||||
* @param startingIndex the slot in which to start storing values, and must be either 0 or 1.
|
||||
*/
|
||||
private static void populateValues(String[] values, Cursor cursor, int[] columnIndicies,
|
||||
int startingIndex) {
|
||||
assert startingIndex == 0 || startingIndex == 1;
|
||||
for (int i = 0; i < columnIndicies.length; i++) {
|
||||
values[startingIndex + i*2] = cursor.getString(columnIndicies[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the cursors past the rows indicated in the most recent call to next().
|
||||
* This will only have an affect once per call to next().
|
||||
*/
|
||||
private void incrementCursors() {
|
||||
if (mCompareResultIsValid) {
|
||||
switch (mCompareResult) {
|
||||
case LEFT:
|
||||
mCursorLeft.moveToNext();
|
||||
break;
|
||||
case RIGHT:
|
||||
mCursorRight.moveToNext();
|
||||
break;
|
||||
case BOTH:
|
||||
mCursorLeft.moveToNext();
|
||||
mCursorRight.moveToNext();
|
||||
break;
|
||||
}
|
||||
mCompareResultIsValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the values. Values contains n pairs of strings. If all the pairs of strings match
|
||||
* then returns 0. Otherwise returns the comparison result of the first non-matching pair
|
||||
* of values, -1 if the first of the pair is less than the second of the pair or 1 if it
|
||||
* is greater.
|
||||
* @param values the n pairs of values to compare
|
||||
* @return -1, 0, or 1 as described above.
|
||||
*/
|
||||
private static int compareStrings(String... values) {
|
||||
if ((values.length % 2) != 0) {
|
||||
throw new IllegalArgumentException("you must specify an even number of values");
|
||||
}
|
||||
|
||||
for (int index = 0; index < values.length; index+=2) {
|
||||
if (values[index] == null) {
|
||||
if (values[index+1] == null) continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (values[index+1] == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int comp = values[index].compareTo(values[index+1]);
|
||||
if (comp != 0) {
|
||||
return comp < 0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.database.sqlite.SQLiteMisuseException;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a BulkCursor around an existing Cursor making it remotable.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
implements IBinder.DeathRecipient {
|
||||
private static final String TAG = "Cursor";
|
||||
private final CrossProcessCursor mCursor;
|
||||
private CursorWindow mWindow;
|
||||
private final String mProviderName;
|
||||
private final boolean mReadOnly;
|
||||
private ContentObserverProxy mObserver;
|
||||
|
||||
private static final class ContentObserverProxy extends ContentObserver
|
||||
{
|
||||
protected IContentObserver mRemote;
|
||||
|
||||
public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
|
||||
super(null);
|
||||
mRemote = remoteObserver;
|
||||
try {
|
||||
remoteObserver.asBinder().linkToDeath(recipient, 0);
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing, the far side is dead
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlinkToDeath(DeathRecipient recipient) {
|
||||
return mRemote.asBinder().unlinkToDeath(recipient, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deliverSelfNotifications() {
|
||||
// The far side handles the self notifications.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
try {
|
||||
mRemote.onChange(selfChange);
|
||||
} catch (RemoteException ex) {
|
||||
// Do nothing, the far side is dead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
|
||||
boolean allowWrite, CursorWindow window) {
|
||||
try {
|
||||
mCursor = (CrossProcessCursor) cursor;
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
|
||||
if (windowedCursor.hasWindow()) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) {
|
||||
Log.v(TAG, "Cross process cursor has a local window before setWindow in "
|
||||
+ providerName, new RuntimeException());
|
||||
}
|
||||
}
|
||||
windowedCursor.setWindow(window);
|
||||
} else {
|
||||
mWindow = window;
|
||||
mCursor.fillWindow(0, window);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
// TODO Implement this case.
|
||||
throw new UnsupportedOperationException(
|
||||
"Only CrossProcessCursor cursors are supported across process for now", e);
|
||||
}
|
||||
mProviderName = providerName;
|
||||
mReadOnly = !allowWrite;
|
||||
|
||||
createAndRegisterObserverProxy(observer);
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
mCursor.close();
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
}
|
||||
}
|
||||
|
||||
public CursorWindow getWindow(int startPos) {
|
||||
mCursor.moveToPosition(startPos);
|
||||
|
||||
if (mWindow != null) {
|
||||
if (startPos < mWindow.getStartPosition() ||
|
||||
startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
mCursor.fillWindow(startPos, mWindow);
|
||||
}
|
||||
return mWindow;
|
||||
} else {
|
||||
return ((AbstractWindowedCursor)mCursor).getWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public void onMove(int position) {
|
||||
mCursor.onMove(mCursor.getPosition(), position);
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
public String[] getColumnNames() {
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
maybeUnregisterObserverProxy();
|
||||
mCursor.deactivate();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
maybeUnregisterObserverProxy();
|
||||
mCursor.deactivate();
|
||||
|
||||
}
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) {
|
||||
if (mWindow == null) {
|
||||
((AbstractWindowedCursor)mCursor).setWindow(window);
|
||||
}
|
||||
try {
|
||||
if (!mCursor.requery()) {
|
||||
return -1;
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
IllegalStateException leakProgram = new IllegalStateException(
|
||||
mProviderName + " Requery misuse db, mCursor isClosed:" +
|
||||
mCursor.isClosed(), e);
|
||||
throw leakProgram;
|
||||
}
|
||||
|
||||
if (mWindow != null) {
|
||||
mCursor.fillWindow(0, window);
|
||||
mWindow = window;
|
||||
}
|
||||
maybeUnregisterObserverProxy();
|
||||
createAndRegisterObserverProxy(observer);
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() {
|
||||
return mCursor.getWantsAllOnMoveCalls();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContentObserver from the observer and register it as an observer on the
|
||||
* underlying cursor.
|
||||
* @param observer the IContentObserver that wants to monitor the cursor
|
||||
* @throws IllegalStateException if an observer is already registered
|
||||
*/
|
||||
private void createAndRegisterObserverProxy(IContentObserver observer) {
|
||||
if (mObserver != null) {
|
||||
throw new IllegalStateException("an observer is already registered");
|
||||
}
|
||||
mObserver = new ContentObserverProxy(observer, this);
|
||||
mCursor.registerContentObserver(mObserver);
|
||||
}
|
||||
|
||||
/** Unregister the observer if it is already registered. */
|
||||
private void maybeUnregisterObserverProxy() {
|
||||
if (mObserver != null) {
|
||||
mCursor.unregisterContentObserver(mObserver);
|
||||
mObserver.unlinkToDeath(this);
|
||||
mObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) {
|
||||
if (mReadOnly) {
|
||||
Log.w("ContentProvider", "Permission Denial: modifying "
|
||||
+ mProviderName
|
||||
+ " from pid=" + Binder.getCallingPid()
|
||||
+ ", uid=" + Binder.getCallingUid());
|
||||
return false;
|
||||
}
|
||||
return mCursor.commitUpdates(values);
|
||||
}
|
||||
|
||||
public boolean deleteRow(int position) {
|
||||
if (mReadOnly) {
|
||||
Log.w("ContentProvider", "Permission Denial: modifying "
|
||||
+ mProviderName
|
||||
+ " from pid=" + Binder.getCallingPid()
|
||||
+ ", uid=" + Binder.getCallingUid());
|
||||
return false;
|
||||
}
|
||||
if (mCursor.moveToPosition(position) == false) {
|
||||
return false;
|
||||
}
|
||||
return mCursor.deleteRow();
|
||||
}
|
||||
|
||||
public Bundle getExtras() {
|
||||
return mCursor.getExtras();
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) {
|
||||
return mCursor.respond(extras);
|
||||
}
|
||||
}
|
||||
@@ -1,483 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.database.sqlite.SQLiteClosable;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A buffer containing multiple cursor rows.
|
||||
*/
|
||||
public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
/** The pointer to the native window class */
|
||||
@SuppressWarnings("unused")
|
||||
private int nWindow;
|
||||
|
||||
private int mStartPos;
|
||||
|
||||
/**
|
||||
* Creates a new empty window.
|
||||
*
|
||||
* @param localWindow true if this window will be used in this process only
|
||||
*/
|
||||
public CursorWindow(boolean localWindow) {
|
||||
mStartPos = 0;
|
||||
native_init(localWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting position of this window within the entire
|
||||
* Cursor's result set.
|
||||
*
|
||||
* @return the starting position of this window within the entire
|
||||
* Cursor's result set.
|
||||
*/
|
||||
public int getStartPosition() {
|
||||
return mStartPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start position of cursor window
|
||||
* @param pos
|
||||
*/
|
||||
public void setStartPosition(int pos) {
|
||||
mStartPos = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in this window.
|
||||
*
|
||||
* @return the number of rows in this window.
|
||||
*/
|
||||
public int getNumRows() {
|
||||
acquireReference();
|
||||
try {
|
||||
return getNumRows_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native int getNumRows_native();
|
||||
/**
|
||||
* Set number of Columns
|
||||
* @param columnNum
|
||||
* @return true if success
|
||||
*/
|
||||
public boolean setNumColumns(int columnNum) {
|
||||
acquireReference();
|
||||
try {
|
||||
return setNumColumns_native(columnNum);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean setNumColumns_native(int columnNum);
|
||||
|
||||
/**
|
||||
* Allocate a row in cursor window
|
||||
* @return false if cursor window is out of memory
|
||||
*/
|
||||
public boolean allocRow(){
|
||||
acquireReference();
|
||||
try {
|
||||
return allocRow_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean allocRow_native();
|
||||
|
||||
/**
|
||||
* Free the last row
|
||||
*/
|
||||
public void freeLastRow(){
|
||||
acquireReference();
|
||||
try {
|
||||
freeLastRow_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native void freeLastRow_native();
|
||||
|
||||
/**
|
||||
* copy byte array to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putBlob(byte[] value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putBlob_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putBlob_native(byte[] value, int row, int col);
|
||||
|
||||
/**
|
||||
* Copy String to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putString(String value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putString_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putString_native(String value, int row, int col);
|
||||
|
||||
/**
|
||||
* Copy integer to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putLong(long value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putLong_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putLong_native(long value, int row, int col);
|
||||
|
||||
|
||||
/**
|
||||
* Copy double to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putDouble(double value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putDouble_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putDouble_native(double value, int row, int col);
|
||||
|
||||
/**
|
||||
* Set the [row, col] value to NULL
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putNull(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putNull_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putNull_native(int row, int col);
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if given field is {@code NULL}.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is {@code NULL}
|
||||
*/
|
||||
public boolean isNull(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isNull_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean isNull_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a byte array for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a String value for the given field
|
||||
*/
|
||||
public byte[] getBlob(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getBlob_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native byte[] getBlob_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Checks if a field contains either a blob or is null.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is {@code NULL} or a blob
|
||||
*/
|
||||
public boolean isBlob(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isBlob_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean isBlob_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a String for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a String value for the given field
|
||||
*/
|
||||
public String getString(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getString_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native String getString_native(int row, int col);
|
||||
|
||||
/**
|
||||
* copy the text for the given field in the provided char array.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @param buffer the CharArrayBuffer to copy the text into,
|
||||
* If the requested string is larger than the buffer
|
||||
* a new char buffer will be created to hold the string. and assigne to
|
||||
* CharArrayBuffer.data
|
||||
*/
|
||||
public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) {
|
||||
if (buffer == null) {
|
||||
throw new IllegalArgumentException("CharArrayBuffer should not be null");
|
||||
}
|
||||
if (buffer.data == null) {
|
||||
buffer.data = new char[64];
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
char[] newbuf = copyStringToBuffer_native(
|
||||
row - mStartPos, col, buffer.data.length, buffer);
|
||||
if (newbuf != null) {
|
||||
buffer.data = newbuf;
|
||||
}
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native char[] copyStringToBuffer_native(
|
||||
int row, int col, int bufferSize, CharArrayBuffer buffer);
|
||||
|
||||
/**
|
||||
* Returns a long for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a long value for the given field
|
||||
*/
|
||||
public long getLong(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native long getLong_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a double for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a double value for the given field
|
||||
*/
|
||||
public double getDouble(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getDouble_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native double getDouble_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a short for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a short value for the given field
|
||||
*/
|
||||
public short getShort(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (short) getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an int for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return an int value for the given field
|
||||
*/
|
||||
public int getInt(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (int) getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a float value for the given field
|
||||
*/
|
||||
public float getFloat(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (float) getDouble_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the existing contents of the window, making it safe to reuse
|
||||
* for new data. Note that the number of columns in the window may NOT
|
||||
* change across a call to clear().
|
||||
*/
|
||||
public void clear() {
|
||||
acquireReference();
|
||||
try {
|
||||
mStartPos = 0;
|
||||
native_clear();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears out the native side of things */
|
||||
private native void native_clear();
|
||||
|
||||
/**
|
||||
* Cleans up the native resources associated with the window.
|
||||
*/
|
||||
public void close() {
|
||||
releaseReference();
|
||||
}
|
||||
|
||||
private native void close_native();
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
// Just in case someone forgot to call close...
|
||||
close_native();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CursorWindow> CREATOR
|
||||
= new Parcelable.Creator<CursorWindow>() {
|
||||
public CursorWindow createFromParcel(Parcel source) {
|
||||
return new CursorWindow(source);
|
||||
}
|
||||
|
||||
public CursorWindow[] newArray(int size) {
|
||||
return new CursorWindow[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static CursorWindow newFromParcel(Parcel p) {
|
||||
return CREATOR.createFromParcel(p);
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(native_getBinder());
|
||||
dest.writeInt(mStartPos);
|
||||
}
|
||||
|
||||
private CursorWindow(Parcel source) {
|
||||
IBinder nativeBinder = source.readStrongBinder();
|
||||
mStartPos = source.readInt();
|
||||
|
||||
native_init(nativeBinder);
|
||||
}
|
||||
|
||||
/** Get the binder for the native side of the window */
|
||||
private native IBinder native_getBinder();
|
||||
|
||||
/** Does the native side initialization for an empty window */
|
||||
private native void native_init(boolean localOnly);
|
||||
|
||||
/** Does the native side initialization with an existing binder from another process */
|
||||
private native void native_init(IBinder nativeBinder);
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleased() {
|
||||
close_native();
|
||||
}
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.CharArrayBuffer;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wrapper class for Cursor that delegates all calls to the actual cursor object
|
||||
*/
|
||||
|
||||
public class CursorWrapper implements Cursor {
|
||||
|
||||
public CursorWrapper(Cursor cursor) {
|
||||
mCursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public void abortUpdates() {
|
||||
mCursor.abortUpdates();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mCursor.close();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return mCursor.isClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates() {
|
||||
return mCursor.commitUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates(
|
||||
Map<? extends Long, ? extends Map<String, Object>> values) {
|
||||
return mCursor.commitUpdates(values);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
mCursor.deactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean deleteRow() {
|
||||
return mCursor.deleteRow();
|
||||
}
|
||||
|
||||
public boolean moveToFirst() {
|
||||
return mCursor.moveToFirst();
|
||||
}
|
||||
|
||||
public int getColumnCount() {
|
||||
return mCursor.getColumnCount();
|
||||
}
|
||||
|
||||
public int getColumnIndex(String columnName) {
|
||||
return mCursor.getColumnIndex(columnName);
|
||||
}
|
||||
|
||||
public int getColumnIndexOrThrow(String columnName)
|
||||
throws IllegalArgumentException {
|
||||
return mCursor.getColumnIndexOrThrow(columnName);
|
||||
}
|
||||
|
||||
public String getColumnName(int columnIndex) {
|
||||
return mCursor.getColumnName(columnIndex);
|
||||
}
|
||||
|
||||
public String[] getColumnNames() {
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
|
||||
public double getDouble(int columnIndex) {
|
||||
return mCursor.getDouble(columnIndex);
|
||||
}
|
||||
|
||||
public Bundle getExtras() {
|
||||
return mCursor.getExtras();
|
||||
}
|
||||
|
||||
public float getFloat(int columnIndex) {
|
||||
return mCursor.getFloat(columnIndex);
|
||||
}
|
||||
|
||||
public int getInt(int columnIndex) {
|
||||
return mCursor.getInt(columnIndex);
|
||||
}
|
||||
|
||||
public long getLong(int columnIndex) {
|
||||
return mCursor.getLong(columnIndex);
|
||||
}
|
||||
|
||||
public short getShort(int columnIndex) {
|
||||
return mCursor.getShort(columnIndex);
|
||||
}
|
||||
|
||||
public String getString(int columnIndex) {
|
||||
return mCursor.getString(columnIndex);
|
||||
}
|
||||
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
||||
mCursor.copyStringToBuffer(columnIndex, buffer);
|
||||
}
|
||||
|
||||
public byte[] getBlob(int columnIndex) {
|
||||
return mCursor.getBlob(columnIndex);
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() {
|
||||
return mCursor.getWantsAllOnMoveCalls();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean hasUpdates() {
|
||||
return mCursor.hasUpdates();
|
||||
}
|
||||
|
||||
public boolean isAfterLast() {
|
||||
return mCursor.isAfterLast();
|
||||
}
|
||||
|
||||
public boolean isBeforeFirst() {
|
||||
return mCursor.isBeforeFirst();
|
||||
}
|
||||
|
||||
public boolean isFirst() {
|
||||
return mCursor.isFirst();
|
||||
}
|
||||
|
||||
public boolean isLast() {
|
||||
return mCursor.isLast();
|
||||
}
|
||||
|
||||
public boolean isNull(int columnIndex) {
|
||||
return mCursor.isNull(columnIndex);
|
||||
}
|
||||
|
||||
public boolean moveToLast() {
|
||||
return mCursor.moveToLast();
|
||||
}
|
||||
|
||||
public boolean move(int offset) {
|
||||
return mCursor.move(offset);
|
||||
}
|
||||
|
||||
public boolean moveToPosition(int position) {
|
||||
return mCursor.moveToPosition(position);
|
||||
}
|
||||
|
||||
public boolean moveToNext() {
|
||||
return mCursor.moveToNext();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return mCursor.getPosition();
|
||||
}
|
||||
|
||||
public boolean moveToPrevious() {
|
||||
return mCursor.moveToPrevious();
|
||||
}
|
||||
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
mCursor.registerContentObserver(observer);
|
||||
}
|
||||
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
mCursor.registerDataSetObserver(observer);
|
||||
}
|
||||
|
||||
public boolean requery() {
|
||||
return mCursor.requery();
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) {
|
||||
return mCursor.respond(extras);
|
||||
}
|
||||
|
||||
public void setNotificationUri(ContentResolver cr, Uri uri) {
|
||||
mCursor.setNotificationUri(cr, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean supportsUpdates() {
|
||||
return mCursor.supportsUpdates();
|
||||
}
|
||||
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
mCursor.unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
mCursor.unregisterDataSetObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateDouble(int columnIndex, double value) {
|
||||
return mCursor.updateDouble(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateFloat(int columnIndex, float value) {
|
||||
return mCursor.updateFloat(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateInt(int columnIndex, int value) {
|
||||
return mCursor.updateInt(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateLong(int columnIndex, long value) {
|
||||
return mCursor.updateLong(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateShort(int columnIndex, short value) {
|
||||
return mCursor.updateShort(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateString(int columnIndex, String value) {
|
||||
return mCursor.updateString(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateBlob(int columnIndex, byte[] value) {
|
||||
return mCursor.updateBlob(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateToNull(int columnIndex) {
|
||||
return mCursor.updateToNull(columnIndex);
|
||||
}
|
||||
|
||||
private Cursor mCursor;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
/**
|
||||
* A specialization of Observable for DataSetObserver that provides methods for
|
||||
* invoking the various callback methods of DataSetObserver.
|
||||
*/
|
||||
public class DataSetObservable extends Observable<DataSetObserver> {
|
||||
/**
|
||||
* Invokes onChanged on each observer. Called when the data set being observed has
|
||||
* changed, and which when read contains the new state of the data.
|
||||
*/
|
||||
public void notifyChanged() {
|
||||
synchronized(mObservers) {
|
||||
for (DataSetObserver observer : mObservers) {
|
||||
observer.onChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes onInvalidated on each observer. Called when the data set being monitored
|
||||
* has changed such that it is no longer valid.
|
||||
*/
|
||||
public void notifyInvalidated() {
|
||||
synchronized (mObservers) {
|
||||
for (DataSetObserver observer : mObservers) {
|
||||
observer.onInvalidated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
/**
|
||||
* Receives call backs when a data set has been changed, or made invalid. The typically data sets
|
||||
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
|
||||
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
|
||||
*/
|
||||
public abstract class DataSetObserver {
|
||||
/**
|
||||
* This method is called when the entire data set has changed,
|
||||
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
|
||||
*/
|
||||
public void onChanged() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the entire data becomes invalid,
|
||||
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
|
||||
* {@link Cursor}.
|
||||
*/
|
||||
public void onInvalidated() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface provides a low-level way to pass bulk cursor data across
|
||||
* both process and language boundries. Application code should use the Cursor
|
||||
* interface directly.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public interface IBulkCursor extends IInterface
|
||||
{
|
||||
/**
|
||||
* Returns a BulkCursorWindow, which either has a reference to a shared
|
||||
* memory segment with the rows, or an array of JSON strings.
|
||||
*/
|
||||
public CursorWindow getWindow(int startPos) throws RemoteException;
|
||||
|
||||
public void onMove(int position) throws RemoteException;
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the cursor.
|
||||
*
|
||||
* @return the number of rows in the cursor.
|
||||
*/
|
||||
public int count() throws RemoteException;
|
||||
|
||||
/**
|
||||
* Returns a string array holding the names of all of the columns in the
|
||||
* cursor in the order in which they were listed in the result.
|
||||
*
|
||||
* @return the names of the columns returned in this query.
|
||||
*/
|
||||
public String[] getColumnNames() throws RemoteException;
|
||||
|
||||
public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) throws RemoteException;
|
||||
|
||||
public boolean deleteRow(int position) throws RemoteException;
|
||||
|
||||
public void deactivate() throws RemoteException;
|
||||
|
||||
public void close() throws RemoteException;
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException;
|
||||
|
||||
boolean getWantsAllOnMoveCalls() throws RemoteException;
|
||||
|
||||
Bundle getExtras() throws RemoteException;
|
||||
|
||||
Bundle respond(Bundle extras) throws RemoteException;
|
||||
|
||||
/* IPC constants */
|
||||
static final String descriptor = "android.content.IBulkCursor";
|
||||
|
||||
static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
|
||||
static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
|
||||
static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
|
||||
static final int UPDATE_ROWS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
|
||||
static final int DELETE_ROW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
|
||||
static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
|
||||
static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
|
||||
static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7;
|
||||
static final int WANTS_ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 8;
|
||||
static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
|
||||
static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
|
||||
static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11;
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
**
|
||||
** Copyright 2007, 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 android.database;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface IContentObserver
|
||||
{
|
||||
/**
|
||||
* This method is called when an update occurs to the cursor that is being
|
||||
* observed. selfUpdate is true if the update was caused by a call to
|
||||
* commit on the cursor that is being observed.
|
||||
*/
|
||||
oneway void onChange(boolean selfUpdate);
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A mutable cursor implementation backed by an array of {@code Object}s. Use
|
||||
* {@link #newRow()} to add rows. Automatically expands internal capacity
|
||||
* as needed.
|
||||
*/
|
||||
public class MatrixCursor extends AbstractCursor {
|
||||
|
||||
private final String[] columnNames;
|
||||
private Object[] data;
|
||||
private int rowCount = 0;
|
||||
private final int columnCount;
|
||||
|
||||
/**
|
||||
* Constructs a new cursor with the given initial capacity.
|
||||
*
|
||||
* @param columnNames names of the columns, the ordering of which
|
||||
* determines column ordering elsewhere in this cursor
|
||||
* @param initialCapacity in rows
|
||||
*/
|
||||
public MatrixCursor(String[] columnNames, int initialCapacity) {
|
||||
this.columnNames = columnNames;
|
||||
this.columnCount = columnNames.length;
|
||||
|
||||
if (initialCapacity < 1) {
|
||||
initialCapacity = 1;
|
||||
}
|
||||
|
||||
this.data = new Object[columnCount * initialCapacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new cursor.
|
||||
*
|
||||
* @param columnNames names of the columns, the ordering of which
|
||||
* determines column ordering elsewhere in this cursor
|
||||
*/
|
||||
public MatrixCursor(String[] columnNames) {
|
||||
this(columnNames, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value at the given column for the current row.
|
||||
*/
|
||||
private Object get(int column) {
|
||||
if (column < 0 || column >= columnCount) {
|
||||
throw new CursorIndexOutOfBoundsException("Requested column: "
|
||||
+ column + ", # of columns: " + columnCount);
|
||||
}
|
||||
if (mPos < 0) {
|
||||
throw new CursorIndexOutOfBoundsException("Before first row.");
|
||||
}
|
||||
if (mPos >= rowCount) {
|
||||
throw new CursorIndexOutOfBoundsException("After last row.");
|
||||
}
|
||||
return data[mPos * columnCount + column];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end and returns a builder for that row. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @return builder which can be used to set the column values for the new
|
||||
* row
|
||||
*/
|
||||
public RowBuilder newRow() {
|
||||
rowCount++;
|
||||
int endIndex = rowCount * columnCount;
|
||||
ensureCapacity(endIndex);
|
||||
int start = endIndex - columnCount;
|
||||
return new RowBuilder(start, endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end with the given column values. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code columnValues.length !=
|
||||
* columnNames.length}
|
||||
* @param columnValues in the same order as the the column names specified
|
||||
* at cursor construction time
|
||||
*/
|
||||
public void addRow(Object[] columnValues) {
|
||||
if (columnValues.length != columnCount) {
|
||||
throw new IllegalArgumentException("columnNames.length = "
|
||||
+ columnCount + ", columnValues.length = "
|
||||
+ columnValues.length);
|
||||
}
|
||||
|
||||
int start = rowCount++ * columnCount;
|
||||
ensureCapacity(start + columnCount);
|
||||
System.arraycopy(columnValues, 0, data, start, columnCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end with the given column values. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code columnValues.size() !=
|
||||
* columnNames.length}
|
||||
* @param columnValues in the same order as the the column names specified
|
||||
* at cursor construction time
|
||||
*/
|
||||
public void addRow(Iterable<?> columnValues) {
|
||||
int start = rowCount * columnCount;
|
||||
int end = start + columnCount;
|
||||
ensureCapacity(end);
|
||||
|
||||
if (columnValues instanceof ArrayList<?>) {
|
||||
addRow((ArrayList<?>) columnValues, start);
|
||||
return;
|
||||
}
|
||||
|
||||
int current = start;
|
||||
Object[] localData = data;
|
||||
for (Object columnValue : columnValues) {
|
||||
if (current == end) {
|
||||
// TODO: null out row?
|
||||
throw new IllegalArgumentException(
|
||||
"columnValues.size() > columnNames.length");
|
||||
}
|
||||
localData[current++] = columnValue;
|
||||
}
|
||||
|
||||
if (current != end) {
|
||||
// TODO: null out row?
|
||||
throw new IllegalArgumentException(
|
||||
"columnValues.size() < columnNames.length");
|
||||
}
|
||||
|
||||
// Increase row count here in case we encounter an exception.
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
/** Optimization for {@link ArrayList}. */
|
||||
private void addRow(ArrayList<?> columnValues, int start) {
|
||||
int size = columnValues.size();
|
||||
if (size != columnCount) {
|
||||
throw new IllegalArgumentException("columnNames.length = "
|
||||
+ columnCount + ", columnValues.size() = " + size);
|
||||
}
|
||||
|
||||
rowCount++;
|
||||
Object[] localData = data;
|
||||
for (int i = 0; i < size; i++) {
|
||||
localData[start + i] = columnValues.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures that this cursor has enough capacity. */
|
||||
private void ensureCapacity(int size) {
|
||||
if (size > data.length) {
|
||||
Object[] oldData = this.data;
|
||||
int newSize = data.length * 2;
|
||||
if (newSize < size) {
|
||||
newSize = size;
|
||||
}
|
||||
this.data = new Object[newSize];
|
||||
System.arraycopy(oldData, 0, this.data, 0, oldData.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a row, starting from the left-most column and adding one column
|
||||
* value at a time. Follows the same ordering as the column names specified
|
||||
* at cursor construction time.
|
||||
*/
|
||||
public class RowBuilder {
|
||||
|
||||
private int index;
|
||||
private final int endIndex;
|
||||
|
||||
RowBuilder(int index, int endIndex) {
|
||||
this.index = index;
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next column value in this row.
|
||||
*
|
||||
* @throws CursorIndexOutOfBoundsException if you try to add too many
|
||||
* values
|
||||
* @return this builder to support chaining
|
||||
*/
|
||||
public RowBuilder add(Object columnValue) {
|
||||
if (index == endIndex) {
|
||||
throw new CursorIndexOutOfBoundsException(
|
||||
"No more columns left.");
|
||||
}
|
||||
|
||||
data[index++] = columnValue;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// AbstractCursor implementation.
|
||||
|
||||
public int getCount() {
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
public String[] getColumnNames() {
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public String getString(int column) {
|
||||
return String.valueOf(get(column));
|
||||
}
|
||||
|
||||
public short getShort(int column) {
|
||||
Object value = get(column);
|
||||
return (value instanceof String)
|
||||
? Short.valueOf((String) value)
|
||||
: ((Number) value).shortValue();
|
||||
}
|
||||
|
||||
public int getInt(int column) {
|
||||
Object value = get(column);
|
||||
return (value instanceof String)
|
||||
? Integer.valueOf((String) value)
|
||||
: ((Number) value).intValue();
|
||||
}
|
||||
|
||||
public long getLong(int column) {
|
||||
Object value = get(column);
|
||||
return (value instanceof String)
|
||||
? Long.valueOf((String) value)
|
||||
: ((Number) value).longValue();
|
||||
}
|
||||
|
||||
public float getFloat(int column) {
|
||||
Object value = get(column);
|
||||
return (value instanceof String)
|
||||
? Float.valueOf((String) value)
|
||||
: ((Number) value).floatValue();
|
||||
}
|
||||
|
||||
public double getDouble(int column) {
|
||||
Object value = get(column);
|
||||
return (value instanceof String)
|
||||
? Double.valueOf((String) value)
|
||||
: ((Number) value).doubleValue();
|
||||
}
|
||||
|
||||
public boolean isNull(int column) {
|
||||
return get(column) == null;
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
/**
|
||||
* A convience class that lets you present an array of Cursors as a single linear Cursor.
|
||||
* The schema of the cursors presented is entirely up to the creator of the MergeCursor, and
|
||||
* may be different if that is desired. Calls to getColumns, getColumnIndex, etc will return the
|
||||
* value for the row that the MergeCursor is currently pointing at.
|
||||
*/
|
||||
public class MergeCursor extends AbstractCursor
|
||||
{
|
||||
private DataSetObserver mObserver = new DataSetObserver() {
|
||||
|
||||
@Override
|
||||
public void onChanged() {
|
||||
// Reset our position so the optimizations in move-related code
|
||||
// don't screw us over
|
||||
mPos = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
mPos = -1;
|
||||
}
|
||||
};
|
||||
|
||||
public MergeCursor(Cursor[] cursors)
|
||||
{
|
||||
mCursors = cursors;
|
||||
mCursor = cursors[0];
|
||||
|
||||
for (int i = 0; i < mCursors.length; i++) {
|
||||
if (mCursors[i] == null) continue;
|
||||
|
||||
mCursors[i].registerDataSetObserver(mObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
int count = 0;
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
count += mCursors[i].getCount();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition)
|
||||
{
|
||||
/* Find the right cursor */
|
||||
mCursor = null;
|
||||
int cursorStartPos = 0;
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length; i++) {
|
||||
if (mCursors[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newPosition < (cursorStartPos + mCursors[i].getCount())) {
|
||||
mCursor = mCursors[i];
|
||||
break;
|
||||
}
|
||||
|
||||
cursorStartPos += mCursors[i].getCount();
|
||||
}
|
||||
|
||||
/* Move it to the right position */
|
||||
if (mCursor != null) {
|
||||
boolean ret = mCursor.moveToPosition(newPosition - cursorStartPos);
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean deleteRow()
|
||||
{
|
||||
return mCursor.deleteRow();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean commitUpdates() {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].commitUpdates();
|
||||
}
|
||||
}
|
||||
onChange(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int column)
|
||||
{
|
||||
return mCursor.getString(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int column)
|
||||
{
|
||||
return mCursor.getShort(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int column)
|
||||
{
|
||||
return mCursor.getInt(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int column)
|
||||
{
|
||||
return mCursor.getLong(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int column)
|
||||
{
|
||||
return mCursor.getFloat(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int column)
|
||||
{
|
||||
return mCursor.getDouble(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int column)
|
||||
{
|
||||
return mCursor.isNull(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBlob(int column)
|
||||
{
|
||||
return mCursor.getBlob(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames()
|
||||
{
|
||||
if (mCursor != null) {
|
||||
return mCursor.getColumnNames();
|
||||
} else {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate()
|
||||
{
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] == null) continue;
|
||||
mCursors[i].close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].registerContentObserver(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].unregisterContentObserver(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].registerDataSetObserver(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] != null) {
|
||||
mCursors[i].unregisterDataSetObserver(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery()
|
||||
{
|
||||
int length = mCursors.length;
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
if (mCursors[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mCursors[i].requery() == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Cursor mCursor; // updated in onMove
|
||||
private Cursor[] mCursors;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Provides methods for (un)registering arbitrary observers in an ArrayList.
|
||||
*/
|
||||
public abstract class Observable<T> {
|
||||
/**
|
||||
* The list of observers. An observer can be in the list at most
|
||||
* once and will never be null.
|
||||
*/
|
||||
protected final ArrayList<T> mObservers = new ArrayList<T>();
|
||||
|
||||
/**
|
||||
* Adds an observer to the list. The observer cannot be null and it must not already
|
||||
* be registered.
|
||||
* @param observer the observer to register
|
||||
* @throws IllegalArgumentException the observer is null
|
||||
* @throws IllegalStateException the observer is already registered
|
||||
*/
|
||||
public void registerObserver(T observer) {
|
||||
if (observer == null) {
|
||||
throw new IllegalArgumentException("The observer is null.");
|
||||
}
|
||||
synchronized(mObservers) {
|
||||
if (mObservers.contains(observer)) {
|
||||
throw new IllegalStateException("Observer " + observer + " is already registered.");
|
||||
}
|
||||
mObservers.add(observer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously registered observer. The observer must not be null and it
|
||||
* must already have been registered.
|
||||
* @param observer the observer to unregister
|
||||
* @throws IllegalArgumentException the observer is null
|
||||
* @throws IllegalStateException the observer is not yet registered
|
||||
*/
|
||||
public void unregisterObserver(T observer) {
|
||||
if (observer == null) {
|
||||
throw new IllegalArgumentException("The observer is null.");
|
||||
}
|
||||
synchronized(mObservers) {
|
||||
int index = mObservers.indexOf(observer);
|
||||
if (index == -1) {
|
||||
throw new IllegalStateException("Observer " + observer + " was not registered.");
|
||||
}
|
||||
mObservers.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all registered observer
|
||||
*/
|
||||
public void unregisterAll() {
|
||||
synchronized(mObservers) {
|
||||
mObservers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates there was an error with SQL parsing or execution.
|
||||
*/
|
||||
public class SQLException extends RuntimeException
|
||||
{
|
||||
public SQLException() {}
|
||||
|
||||
public SQLException(String error)
|
||||
{
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database;
|
||||
|
||||
/**
|
||||
* This exception is thrown when a Cursor contains stale data and must be
|
||||
* requeried before being used again.
|
||||
*/
|
||||
public class StaleDataException extends java.lang.RuntimeException
|
||||
{
|
||||
public StaleDataException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public StaleDataException(String description)
|
||||
{
|
||||
super(description);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<HTML>
|
||||
<BODY>
|
||||
Contains classes to explore data returned through a content provider.
|
||||
<p>
|
||||
If you need to manage data in a private database, use the {@link
|
||||
android.database.sqlite} classes. These classes are used to manage the {@link
|
||||
android.database.Cursor} object returned from a content provider query. Databases
|
||||
are usually created and opened with {@link android.content.Context#openOrCreateDatabase}
|
||||
To make requests through
|
||||
content providers, you can use the {@link android.content.ContentResolver
|
||||
content.ContentResolver} class.
|
||||
<p>All databases are stored on the device in <code>/data/data/<package_name>/databases</code>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite program was aborted.
|
||||
* This can happen either through a call to ABORT in a trigger,
|
||||
* or as the result of using the ABORT conflict clause.
|
||||
*/
|
||||
public class SQLiteAbortException extends SQLiteException {
|
||||
public SQLiteAbortException() {}
|
||||
|
||||
public SQLiteAbortException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An object create from a SQLiteDatabase that can be closed.
|
||||
*/
|
||||
public abstract class SQLiteClosable {
|
||||
private int mReferenceCount = 1;
|
||||
private Object mLock = new Object();
|
||||
protected abstract void onAllReferencesReleased();
|
||||
protected void onAllReferencesReleasedFromContainer(){}
|
||||
|
||||
public void acquireReference() {
|
||||
synchronized(mLock) {
|
||||
if (mReferenceCount <= 0) {
|
||||
throw new IllegalStateException(
|
||||
"attempt to acquire a reference on a close SQLiteClosable");
|
||||
}
|
||||
mReferenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseReference() {
|
||||
synchronized(mLock) {
|
||||
mReferenceCount--;
|
||||
if (mReferenceCount == 0) {
|
||||
onAllReferencesReleased();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseReferenceFromContainer() {
|
||||
synchronized(mLock) {
|
||||
mReferenceCount--;
|
||||
if (mReferenceCount == 0) {
|
||||
onAllReferencesReleasedFromContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that an integrity constraint was violated.
|
||||
*/
|
||||
public class SQLiteConstraintException extends SQLiteException {
|
||||
public SQLiteConstraintException() {}
|
||||
|
||||
public SQLiteConstraintException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,606 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.database.AbstractWindowedCursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.DataSetObserver;
|
||||
import android.database.SQLException;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* A Cursor implementation that exposes results from a query on a
|
||||
* {@link SQLiteDatabase}.
|
||||
*/
|
||||
public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
static final String TAG = "Cursor";
|
||||
static final int NO_COUNT = -1;
|
||||
|
||||
/** The name of the table to edit */
|
||||
private String mEditTable;
|
||||
|
||||
/** The names of the columns in the rows */
|
||||
private String[] mColumns;
|
||||
|
||||
/** The query object for the cursor */
|
||||
private SQLiteQuery mQuery;
|
||||
|
||||
/** The database the cursor was created from */
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
/** The compiled query this cursor came from */
|
||||
private SQLiteCursorDriver mDriver;
|
||||
|
||||
/** The number of rows in the cursor */
|
||||
private int mCount = NO_COUNT;
|
||||
|
||||
/** A mapping of column names to column indices, to speed up lookups */
|
||||
private Map<String, Integer> mColumnNameMap;
|
||||
|
||||
/** Used to find out where a cursor was allocated in case it never got
|
||||
* released. */
|
||||
private StackTraceElement[] mStackTraceElements;
|
||||
|
||||
/**
|
||||
* mMaxRead is the max items that each cursor window reads
|
||||
* default to a very high value
|
||||
*/
|
||||
private int mMaxRead = Integer.MAX_VALUE;
|
||||
private int mInitialRead = Integer.MAX_VALUE;
|
||||
private int mCursorState = 0;
|
||||
private ReentrantLock mLock = null;
|
||||
private boolean mPendingData = false;
|
||||
|
||||
/**
|
||||
* support for a cursor variant that doesn't always read all results
|
||||
* initialRead is the initial number of items that cursor window reads
|
||||
* if query contains more than this number of items, a thread will be
|
||||
* created and handle the left over items so that caller can show
|
||||
* results as soon as possible
|
||||
* @param initialRead initial number of items that cursor read
|
||||
* @param maxRead leftover items read at maxRead items per time
|
||||
* @hide
|
||||
*/
|
||||
public void setLoadStyle(int initialRead, int maxRead) {
|
||||
mMaxRead = maxRead;
|
||||
mInitialRead = initialRead;
|
||||
mLock = new ReentrantLock(true);
|
||||
}
|
||||
|
||||
private void queryThreadLock() {
|
||||
if (mLock != null) {
|
||||
mLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
private void queryThreadUnlock() {
|
||||
if (mLock != null) {
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
final private class QueryThread implements Runnable {
|
||||
private final int mThreadState;
|
||||
QueryThread(int version) {
|
||||
mThreadState = version;
|
||||
}
|
||||
private void sendMessage() {
|
||||
if (mNotificationHandler != null) {
|
||||
mNotificationHandler.sendEmptyMessage(1);
|
||||
mPendingData = false;
|
||||
} else {
|
||||
mPendingData = true;
|
||||
}
|
||||
|
||||
}
|
||||
public void run() {
|
||||
// use cached mWindow, to avoid get null mWindow
|
||||
CursorWindow cw = mWindow;
|
||||
Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND);
|
||||
// the cursor's state doesn't change
|
||||
while (true) {
|
||||
mLock.lock();
|
||||
if (mCursorState != mThreadState) {
|
||||
mLock.unlock();
|
||||
break;
|
||||
}
|
||||
try {
|
||||
int count = mQuery.fillWindow(cw, mMaxRead, mCount);
|
||||
// return -1 means not finished
|
||||
if (count != 0) {
|
||||
if (count == NO_COUNT){
|
||||
mCount += mMaxRead;
|
||||
sendMessage();
|
||||
} else {
|
||||
mCount = count;
|
||||
sendMessage();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// end the tread when the cursor is close
|
||||
break;
|
||||
} finally {
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected class MainThreadNotificationHandler extends Handler {
|
||||
public void handleMessage(Message msg) {
|
||||
notifyDataSetChange();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected MainThreadNotificationHandler mNotificationHandler;
|
||||
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
super.registerDataSetObserver(observer);
|
||||
if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) &&
|
||||
mNotificationHandler == null) {
|
||||
queryThreadLock();
|
||||
try {
|
||||
mNotificationHandler = new MainThreadNotificationHandler();
|
||||
if (mPendingData) {
|
||||
notifyDataSetChange();
|
||||
mPendingData = false;
|
||||
}
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query and provide access to its result set through a Cursor
|
||||
* interface. For a query such as: {@code SELECT name, birth, phone FROM
|
||||
* myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
|
||||
* phone) would be in the projection argument and everything from
|
||||
* {@code FROM} onward would be in the params argument. This constructor
|
||||
* has package scope.
|
||||
*
|
||||
* @param db a reference to a Database object that is already constructed
|
||||
* and opened
|
||||
* @param editTable the name of the table used for this query
|
||||
* @param query the rest of the query terms
|
||||
* cursor is finalized
|
||||
*/
|
||||
public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
|
||||
String editTable, SQLiteQuery query) {
|
||||
// The AbstractCursor constructor needs to do some setup.
|
||||
super();
|
||||
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
mStackTraceElements = new Exception().getStackTrace();
|
||||
}
|
||||
|
||||
mDatabase = db;
|
||||
mDriver = driver;
|
||||
mEditTable = editTable;
|
||||
mColumnNameMap = null;
|
||||
mQuery = query;
|
||||
|
||||
try {
|
||||
db.lock();
|
||||
|
||||
// Setup the list of columns
|
||||
int columnCount = mQuery.columnCountLocked();
|
||||
mColumns = new String[columnCount];
|
||||
|
||||
// Read in all column names
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
String columnName = mQuery.columnNameLocked(i);
|
||||
mColumns[i] = columnName;
|
||||
if (Config.LOGV) {
|
||||
Log.v("DatabaseWindow", "mColumns[" + i + "] is "
|
||||
+ mColumns[i]);
|
||||
}
|
||||
|
||||
// Make note of the row ID column index for quick access to it
|
||||
if ("_id".equals(columnName)) {
|
||||
mRowIdColumnIndex = i;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
db.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the SQLiteDatabase that this cursor is associated with.
|
||||
*/
|
||||
public SQLiteDatabase getDatabase() {
|
||||
return mDatabase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
// Make sure the row at newPosition is present in the window
|
||||
if (mWindow == null || newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
fillWindow(newPosition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mCount == NO_COUNT) {
|
||||
fillWindow(0);
|
||||
}
|
||||
return mCount;
|
||||
}
|
||||
|
||||
private void fillWindow (int startPos) {
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
} else {
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mWindow.clear();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
}
|
||||
mWindow.setStartPosition(startPos);
|
||||
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
|
||||
// return -1 means not finished
|
||||
if (mCount == NO_COUNT){
|
||||
mCount = startPos + mInitialRead;
|
||||
Thread t = new Thread(new QueryThread(mCursorState), "query thread");
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
// Create mColumnNameMap on demand
|
||||
if (mColumnNameMap == null) {
|
||||
String[] columns = mColumns;
|
||||
int columnCount = columns.length;
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>(columnCount, 1);
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
map.put(columns[i], i);
|
||||
}
|
||||
mColumnNameMap = map;
|
||||
}
|
||||
|
||||
// Hack according to bug 903852
|
||||
final int periodIndex = columnName.lastIndexOf('.');
|
||||
if (periodIndex != -1) {
|
||||
Exception e = new Exception();
|
||||
Log.e(TAG, "requesting column name with table name -- " + columnName, e);
|
||||
columnName = columnName.substring(periodIndex + 1);
|
||||
}
|
||||
|
||||
Integer i = mColumnNameMap.get(columnName);
|
||||
if (i != null) {
|
||||
return i.intValue();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean deleteRow() {
|
||||
checkPosition();
|
||||
|
||||
// Only allow deletes if there is an ID column, and the ID has been read from it
|
||||
if (mRowIdColumnIndex == -1 || mCurrentRowID == null) {
|
||||
Log.e(TAG,
|
||||
"Could not delete row because either the row ID column is not available or it" +
|
||||
"has not been read.");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success;
|
||||
|
||||
/*
|
||||
* Ensure we don't change the state of the database when another
|
||||
* thread is holding the database lock. requery() and moveTo() are also
|
||||
* synchronized here to make sure they get the state of the database
|
||||
* immediately following the DELETE.
|
||||
*/
|
||||
mDatabase.lock();
|
||||
try {
|
||||
try {
|
||||
mDatabase.delete(mEditTable, mColumns[mRowIdColumnIndex] + "=?",
|
||||
new String[] {mCurrentRowID.toString()});
|
||||
success = true;
|
||||
} catch (SQLException e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
int pos = mPos;
|
||||
requery();
|
||||
|
||||
/*
|
||||
* Ensure proper cursor state. Note that mCurrentRowID changes
|
||||
* in this call.
|
||||
*/
|
||||
moveToPosition(pos);
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
|
||||
if (success) {
|
||||
onChange(true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsUpdates() {
|
||||
return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean commitUpdates(Map<? extends Long,
|
||||
? extends Map<String, Object>> additionalValues) {
|
||||
if (!supportsUpdates()) {
|
||||
Log.e(TAG, "commitUpdates not supported on this cursor, did you "
|
||||
+ "include the _id column?");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent other threads from changing the updated rows while they're
|
||||
* being processed here.
|
||||
*/
|
||||
synchronized (mUpdatedRows) {
|
||||
if (additionalValues != null) {
|
||||
mUpdatedRows.putAll(additionalValues);
|
||||
}
|
||||
|
||||
if (mUpdatedRows.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent other threads from changing the database state while
|
||||
* we process the updated rows, and prevents us from changing the
|
||||
* database behind the back of another thread.
|
||||
*/
|
||||
mDatabase.beginTransaction();
|
||||
try {
|
||||
StringBuilder sql = new StringBuilder(128);
|
||||
|
||||
// For each row that has been updated
|
||||
for (Map.Entry<Long, Map<String, Object>> rowEntry :
|
||||
mUpdatedRows.entrySet()) {
|
||||
Map<String, Object> values = rowEntry.getValue();
|
||||
Long rowIdObj = rowEntry.getKey();
|
||||
|
||||
if (rowIdObj == null || values == null) {
|
||||
throw new IllegalStateException("null rowId or values found! rowId = "
|
||||
+ rowIdObj + ", values = " + values);
|
||||
}
|
||||
|
||||
if (values.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long rowId = rowIdObj.longValue();
|
||||
|
||||
Iterator<Map.Entry<String, Object>> valuesIter =
|
||||
values.entrySet().iterator();
|
||||
|
||||
sql.setLength(0);
|
||||
sql.append("UPDATE " + mEditTable + " SET ");
|
||||
|
||||
// For each column value that has been updated
|
||||
Object[] bindings = new Object[values.size()];
|
||||
int i = 0;
|
||||
while (valuesIter.hasNext()) {
|
||||
Map.Entry<String, Object> entry = valuesIter.next();
|
||||
sql.append(entry.getKey());
|
||||
sql.append("=?");
|
||||
bindings[i] = entry.getValue();
|
||||
if (valuesIter.hasNext()) {
|
||||
sql.append(", ");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
sql.append(" WHERE " + mColumns[mRowIdColumnIndex]
|
||||
+ '=' + rowId);
|
||||
sql.append(';');
|
||||
mDatabase.execSQL(sql.toString(), bindings);
|
||||
mDatabase.rowUpdated(mEditTable, rowId);
|
||||
}
|
||||
mDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
mDatabase.endTransaction();
|
||||
}
|
||||
|
||||
mUpdatedRows.clear();
|
||||
}
|
||||
|
||||
// Let any change observers know about the update
|
||||
onChange(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deactivateCommon() {
|
||||
if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
|
||||
mCursorState = 0;
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
mWindow = null;
|
||||
}
|
||||
if (Config.LOGV) Log.v("DatabaseWindow", "closing window in release()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
deactivateCommon();
|
||||
mDriver.cursorDeactivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
deactivateCommon();
|
||||
mQuery.close();
|
||||
mDriver.cursorClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery() {
|
||||
if (isClosed()) {
|
||||
return false;
|
||||
}
|
||||
long timeStart = 0;
|
||||
if (Config.LOGV) {
|
||||
timeStart = System.currentTimeMillis();
|
||||
}
|
||||
/*
|
||||
* Synchronize on the database lock to ensure that mCount matches the
|
||||
* results of mQuery.requery().
|
||||
*/
|
||||
mDatabase.lock();
|
||||
try {
|
||||
if (mWindow != null) {
|
||||
mWindow.clear();
|
||||
}
|
||||
mPos = -1;
|
||||
// This one will recreate the temp table, and get its count
|
||||
mDriver.cursorRequeried(this);
|
||||
mCount = NO_COUNT;
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mQuery.requery();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
|
||||
if (Config.LOGV) {
|
||||
Log.v("DatabaseWindow", "closing window in requery()");
|
||||
Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
|
||||
}
|
||||
|
||||
boolean result = super.requery();
|
||||
if (Config.LOGV) {
|
||||
long timeEnd = System.currentTimeMillis();
|
||||
Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindow(CursorWindow window) {
|
||||
if (mWindow != null) {
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mWindow.close();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
mCount = NO_COUNT;
|
||||
}
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the selection arguments. The new values take effect after a call to requery().
|
||||
*/
|
||||
public void setSelectionArguments(String[] selectionArgs) {
|
||||
mDriver.setBindArguments(selectionArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the native resources, if they haven't been released yet.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() {
|
||||
try {
|
||||
if (mWindow != null) {
|
||||
close();
|
||||
String message = "Finalizing cursor " + this + " on " + mEditTable
|
||||
+ " that has not been deactivated or closed";
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.d(TAG, message + "\nThis cursor was created in:");
|
||||
for (StackTraceElement ste : mStackTraceElements) {
|
||||
Log.d(TAG, " " + ste);
|
||||
}
|
||||
}
|
||||
SQLiteDebug.notifyActiveCursorFinalized();
|
||||
throw new IllegalStateException(message);
|
||||
} else {
|
||||
if (Config.LOGV) {
|
||||
Log.v(TAG, "Finalizing cursor " + this + " on " + mEditTable);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database.sqlite;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||
|
||||
/**
|
||||
* A driver for SQLiteCursors that is used to create them and gets notified
|
||||
* by the cursors it creates on significant events in their lifetimes.
|
||||
*/
|
||||
public interface SQLiteCursorDriver {
|
||||
/**
|
||||
* Executes the query returning a Cursor over the result set.
|
||||
*
|
||||
* @param factory The CursorFactory to use when creating the Cursors, or
|
||||
* null if standard SQLiteCursors should be returned.
|
||||
* @return a Cursor over the result set
|
||||
*/
|
||||
Cursor query(CursorFactory factory, String[] bindArgs);
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it is released.
|
||||
*/
|
||||
void cursorDeactivated();
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it is requeryed.
|
||||
*
|
||||
* @return The new count value.
|
||||
*/
|
||||
void cursorRequeried(Cursor cursor);
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it it closed to destroy this object as well.
|
||||
*/
|
||||
void cursorClosed();
|
||||
|
||||
/**
|
||||
* Set new bind arguments. These will take effect in cursorRequeried().
|
||||
* @param bindArgs the new arguments
|
||||
*/
|
||||
public void setBindArguments(String[] bindArgs);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite database file is corrupt.
|
||||
*/
|
||||
public class SQLiteDatabaseCorruptException extends SQLiteException {
|
||||
public SQLiteDatabaseCorruptException() {}
|
||||
|
||||
public SQLiteDatabaseCorruptException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database.sqlite;
|
||||
|
||||
import android.util.Config;
|
||||
|
||||
/**
|
||||
* Provides debugging info about all SQLite databases running in the current process.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class SQLiteDebug {
|
||||
/**
|
||||
* Controls the printing of SQL statements as they are executed.
|
||||
*/
|
||||
public static final boolean DEBUG_SQL_STATEMENTS = Config.LOGV;
|
||||
|
||||
/**
|
||||
* Controls the stack trace reporting of active cursors being
|
||||
* finalized.
|
||||
*/
|
||||
public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION = Config.LOGV;
|
||||
|
||||
/**
|
||||
* Controls the tracking of time spent holding the database lock.
|
||||
*/
|
||||
public static final boolean DEBUG_LOCK_TIME_TRACKING = false;
|
||||
|
||||
/**
|
||||
* Controls the printing of stack traces when tracking the time spent holding the database lock.
|
||||
*/
|
||||
public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE = false;
|
||||
|
||||
/**
|
||||
* Contains statistics about the active pagers in the current process.
|
||||
*
|
||||
* @see #getPagerStats(PagerStats)
|
||||
*/
|
||||
public static class PagerStats {
|
||||
/** The total number of bytes in all pagers in the current process */
|
||||
public long totalBytes;
|
||||
/** The number of bytes in referenced pages in all pagers in the current process */
|
||||
public long referencedBytes;
|
||||
/** The number of bytes in all database files opened in the current process */
|
||||
public long databaseBytes;
|
||||
/** The number of pagers opened in the current process */
|
||||
public int numPagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers statistics about all pagers in the current process.
|
||||
*/
|
||||
public static native void getPagerStats(PagerStats stats);
|
||||
|
||||
/**
|
||||
* Returns the size of the SQLite heap.
|
||||
* @return The size of the SQLite heap in bytes.
|
||||
*/
|
||||
public static native long getHeapSize();
|
||||
|
||||
/**
|
||||
* Returns the amount of allocated memory in the SQLite heap.
|
||||
* @return The allocated size in bytes.
|
||||
*/
|
||||
public static native long getHeapAllocatedSize();
|
||||
|
||||
/**
|
||||
* Returns the amount of free memory in the SQLite heap.
|
||||
* @return The freed size in bytes.
|
||||
*/
|
||||
public static native long getHeapFreeSize();
|
||||
|
||||
/**
|
||||
* Determines the number of dirty belonging to the SQLite
|
||||
* heap segments of this process. pages[0] returns the number of
|
||||
* shared pages, pages[1] returns the number of private pages
|
||||
*/
|
||||
public static native void getHeapDirtyPages(int[] pages);
|
||||
|
||||
private static int sNumActiveCursorsFinalized = 0;
|
||||
|
||||
/**
|
||||
* Returns the number of active cursors that have been finalized. This depends on the GC having
|
||||
* run but is still useful for tests.
|
||||
*/
|
||||
public static int getNumActiveCursorsFinalized() {
|
||||
return sNumActiveCursorsFinalized;
|
||||
}
|
||||
|
||||
static synchronized void notifyActiveCursorFinalized() {
|
||||
sNumActiveCursorsFinalized++;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database.sqlite;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A cursor driver that uses the given query directly.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
|
||||
private static String TAG = "SQLiteDirectCursorDriver";
|
||||
private String mEditTable;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private Cursor mCursor;
|
||||
private String mSql;
|
||||
private SQLiteQuery mQuery;
|
||||
|
||||
public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable) {
|
||||
mDatabase = db;
|
||||
mEditTable = editTable;
|
||||
//TODO remove all callers that end in ; and remove this check
|
||||
if (sql.charAt(sql.length() - 1) == ';') {
|
||||
Log.w(TAG, "Found SQL string that ends in ; -- " + sql);
|
||||
sql = sql.substring(0, sql.length() - 1);
|
||||
}
|
||||
mSql = sql;
|
||||
}
|
||||
|
||||
public Cursor query(CursorFactory factory, String[] selectionArgs) {
|
||||
// Compile the query
|
||||
SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
|
||||
|
||||
try {
|
||||
// Arg binding
|
||||
int numArgs = selectionArgs == null ? 0 : selectionArgs.length;
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
query.bindString(i + 1, selectionArgs[i]);
|
||||
}
|
||||
|
||||
// Create the cursor
|
||||
if (factory == null) {
|
||||
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
|
||||
} else {
|
||||
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
|
||||
}
|
||||
|
||||
mQuery = query;
|
||||
query = null;
|
||||
return mCursor;
|
||||
} finally {
|
||||
// Make sure this object is cleaned up if something happens
|
||||
if (query != null) query.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void cursorClosed() {
|
||||
mCursor = null;
|
||||
}
|
||||
|
||||
public void setBindArguments(String[] bindArgs) {
|
||||
final int numArgs = bindArgs.length;
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
mQuery.bindString(i + 1, bindArgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void cursorDeactivated() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void cursorRequeried(Cursor cursor) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLiteDirectCursorDriver: " + mSql;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that an IO error occured while accessing the
|
||||
* SQLite database file.
|
||||
*/
|
||||
public class SQLiteDiskIOException extends SQLiteException {
|
||||
public SQLiteDiskIOException() {}
|
||||
|
||||
public SQLiteDiskIOException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite program is done.
|
||||
* Thrown when an operation that expects a row (such as {@link
|
||||
* SQLiteStatement#simpleQueryForString} or {@link
|
||||
* SQLiteStatement#simpleQueryForLong}) does not get one.
|
||||
*/
|
||||
public class SQLiteDoneException extends SQLiteException {
|
||||
public SQLiteDoneException() {}
|
||||
|
||||
public SQLiteDoneException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.database.SQLException;
|
||||
|
||||
/**
|
||||
* A SQLite exception that indicates there was an error with SQL parsing or execution.
|
||||
*/
|
||||
public class SQLiteException extends SQLException {
|
||||
public SQLiteException() {}
|
||||
|
||||
public SQLiteException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database.sqlite;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite database is full.
|
||||
*/
|
||||
public class SQLiteFullException extends SQLiteException {
|
||||
public SQLiteFullException() {}
|
||||
|
||||
public SQLiteFullException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 android.database.sqlite;
|
||||
|
||||
public class SQLiteMisuseException extends SQLiteException {
|
||||
public SQLiteMisuseException() {}
|
||||
|
||||
public SQLiteMisuseException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 android.database.sqlite;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A helper class to manage database creation and version management.
|
||||
* You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
|
||||
* optionally {@link #onOpen}, and this class takes care of opening the database
|
||||
* if it exists, creating it if it does not, and upgrading it as necessary.
|
||||
* Transactions are used to make sure the database is always in a sensible state.
|
||||
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
|
||||
* in the <em>samples/</em> directory of the SDK.</p>
|
||||
*/
|
||||
public abstract class SQLiteOpenHelper {
|
||||
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private final String mName;
|
||||
private final CursorFactory mFactory;
|
||||
private final int mNewVersion;
|
||||
|
||||
private SQLiteDatabase mDatabase = null;
|
||||
private boolean mIsInitializing = false;
|
||||
|
||||
/**
|
||||
* Create a helper object to create, open, and/or manage a database.
|
||||
* The database is not actually created or opened until one of
|
||||
* {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
|
||||
*
|
||||
* @param context to use to open or create the database
|
||||
* @param name of the database file, or null for an in-memory database
|
||||
* @param factory to use for creating cursor objects, or null for the default
|
||||
* @param version number of the database (starting at 1); if the database is older,
|
||||
* {@link #onUpgrade} will be used to upgrade the database
|
||||
*/
|
||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
|
||||
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
|
||||
|
||||
mContext = context;
|
||||
mName = name;
|
||||
mFactory = factory;
|
||||
mNewVersion = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open a database that will be used for reading and writing.
|
||||
* Once opened successfully, the database is cached, so you can call this
|
||||
* method every time you need to write to the database. Make sure to call
|
||||
* {@link #close} when you no longer need it.
|
||||
*
|
||||
* <p>Errors such as bad permissions or a full disk may cause this operation
|
||||
* to fail, but future attempts may succeed if the problem is fixed.</p>
|
||||
*
|
||||
* @throws SQLiteException if the database cannot be opened for writing
|
||||
* @return a read/write database object valid until {@link #close} is called
|
||||
*/
|
||||
public synchronized SQLiteDatabase getWritableDatabase() {
|
||||
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
|
||||
return mDatabase; // The database is already open for business
|
||||
}
|
||||
|
||||
if (mIsInitializing) {
|
||||
throw new IllegalStateException("getWritableDatabase called recursively");
|
||||
}
|
||||
|
||||
// If we have a read-only database open, someone could be using it
|
||||
// (though they shouldn't), which would cause a lock to be held on
|
||||
// the file, and our attempts to open the database read-write would
|
||||
// fail waiting for the file lock. To prevent that, we acquire the
|
||||
// lock on the read-only database, which shuts out other users.
|
||||
|
||||
boolean success = false;
|
||||
SQLiteDatabase db = null;
|
||||
if (mDatabase != null) mDatabase.lock();
|
||||
try {
|
||||
mIsInitializing = true;
|
||||
if (mName == null) {
|
||||
db = SQLiteDatabase.create(null);
|
||||
} else {
|
||||
db = mContext.openOrCreateDatabase(mName, 0, mFactory);
|
||||
}
|
||||
|
||||
int version = db.getVersion();
|
||||
if (version != mNewVersion) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
if (version == 0) {
|
||||
onCreate(db);
|
||||
} else {
|
||||
onUpgrade(db, version, mNewVersion);
|
||||
}
|
||||
db.setVersion(mNewVersion);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
onOpen(db);
|
||||
success = true;
|
||||
return db;
|
||||
} finally {
|
||||
mIsInitializing = false;
|
||||
if (success) {
|
||||
if (mDatabase != null) {
|
||||
try { mDatabase.close(); } catch (Exception e) { }
|
||||
mDatabase.unlock();
|
||||
}
|
||||
mDatabase = db;
|
||||
} else {
|
||||
if (mDatabase != null) mDatabase.unlock();
|
||||
if (db != null) db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open a database. This will be the same object returned by
|
||||
* {@link #getWritableDatabase} unless some problem, such as a full disk,
|
||||
* requires the database to be opened read-only. In that case, a read-only
|
||||
* database object will be returned. If the problem is fixed, a future call
|
||||
* to {@link #getWritableDatabase} may succeed, in which case the read-only
|
||||
* database object will be closed and the read/write object will be returned
|
||||
* in the future.
|
||||
*
|
||||
* @throws SQLiteException if the database cannot be opened
|
||||
* @return a database object valid until {@link #getWritableDatabase}
|
||||
* or {@link #close} is called.
|
||||
*/
|
||||
public synchronized SQLiteDatabase getReadableDatabase() {
|
||||
if (mDatabase != null && mDatabase.isOpen()) {
|
||||
return mDatabase; // The database is already open for business
|
||||
}
|
||||
|
||||
if (mIsInitializing) {
|
||||
throw new IllegalStateException("getReadableDatabase called recursively");
|
||||
}
|
||||
|
||||
try {
|
||||
return getWritableDatabase();
|
||||
} catch (SQLiteException e) {
|
||||
if (mName == null) throw e; // Can't open a temp database read-only!
|
||||
Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
|
||||
}
|
||||
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
mIsInitializing = true;
|
||||
String path = mContext.getDatabasePath(mName).getPath();
|
||||
db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
|
||||
if (db.getVersion() != mNewVersion) {
|
||||
throw new SQLiteException("Can't upgrade read-only database from version " +
|
||||
db.getVersion() + " to " + mNewVersion + ": " + path);
|
||||
}
|
||||
|
||||
onOpen(db);
|
||||
Log.w(TAG, "Opened " + mName + " in read-only mode");
|
||||
mDatabase = db;
|
||||
return mDatabase;
|
||||
} finally {
|
||||
mIsInitializing = false;
|
||||
if (db != null && db != mDatabase) db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close any open database object.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
|
||||
|
||||
if (mDatabase != null && mDatabase.isOpen()) {
|
||||
mDatabase.close();
|
||||
mDatabase = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the database is created for the first time. This is where the
|
||||
* creation of tables and the initial population of the tables should happen.
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public abstract void onCreate(SQLiteDatabase db);
|
||||
|
||||
/**
|
||||
* Called when the database needs to be upgraded. The implementation
|
||||
* should use this method to drop tables, add tables, or do anything else it
|
||||
* needs to upgrade to the new schema version.
|
||||
*
|
||||
* <p>The SQLite ALTER TABLE documentation can be found
|
||||
* <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
|
||||
* you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
|
||||
* you can use ALTER TABLE to rename the old table, then create the new table and then
|
||||
* populate the new table with the contents of the old table.
|
||||
*
|
||||
* @param db The database.
|
||||
* @param oldVersion The old database version.
|
||||
* @param newVersion The new database version.
|
||||
*/
|
||||
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
|
||||
|
||||
/**
|
||||
* Called when the database has been opened.
|
||||
* Override method should check {@link SQLiteDatabase#isReadOnly} before
|
||||
* updating the database.
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public void onOpen(SQLiteDatabase db) {}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A base class for compiled SQLite programs.
|
||||
*/
|
||||
public abstract class SQLiteProgram extends SQLiteClosable {
|
||||
static final String TAG = "SQLiteProgram";
|
||||
|
||||
/** The database this program is compiled against. */
|
||||
protected SQLiteDatabase mDatabase;
|
||||
|
||||
/**
|
||||
* Native linkage, do not modify. This comes from the database and should not be modified
|
||||
* in here or in the native code.
|
||||
*/
|
||||
protected int nHandle = 0;
|
||||
|
||||
/**
|
||||
* Native linkage, do not modify. When non-0 this holds a reference to a valid
|
||||
* sqlite3_statement object. It is only updated by the native code, but may be
|
||||
* checked in this class when the database lock is held to determine if there
|
||||
* is a valid native-side program or not.
|
||||
*/
|
||||
protected int nStatement = 0;
|
||||
|
||||
/**
|
||||
* Used to find out where a cursor was allocated in case it never got
|
||||
* released.
|
||||
*/
|
||||
private StackTraceElement[] mStackTraceElements;
|
||||
|
||||
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
|
||||
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
|
||||
mStackTraceElements = new Exception().getStackTrace();
|
||||
}
|
||||
|
||||
mDatabase = db;
|
||||
db.acquireReference();
|
||||
db.addSQLiteClosable(this);
|
||||
this.nHandle = db.mNativeHandle;
|
||||
compile(sql, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleased() {
|
||||
// Note that native_finalize() checks to make sure that nStatement is
|
||||
// non-null before destroying it.
|
||||
native_finalize();
|
||||
mDatabase.releaseReference();
|
||||
mDatabase.removeSQLiteClosable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleasedFromContainer(){
|
||||
// Note that native_finalize() checks to make sure that nStatement is
|
||||
// non-null before destroying it.
|
||||
native_finalize();
|
||||
mDatabase.releaseReference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique identifier for this program.
|
||||
*
|
||||
* @return a unique identifier for this program
|
||||
*/
|
||||
public final int getUniqueId() {
|
||||
return nStatement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
|
||||
* this method has been called previously without a call to close and forCompilation is set
|
||||
* to false the previous compilation will be used. Setting forceCompilation to true will
|
||||
* always re-compile the program and should be done if you pass differing SQL strings to this
|
||||
* method.
|
||||
*
|
||||
* <P>Note: this method acquires the database lock.</P>
|
||||
*
|
||||
* @param sql the SQL string to compile
|
||||
* @param forceCompilation forces the SQL to be recompiled in the event that there is an
|
||||
* existing compiled SQL program already around
|
||||
*/
|
||||
protected void compile(String sql, boolean forceCompilation) {
|
||||
// Only compile if we don't have a valid statement already or the caller has
|
||||
// explicitly requested a recompile.
|
||||
if (nStatement == 0 || forceCompilation) {
|
||||
mDatabase.lock();
|
||||
try {
|
||||
// Note that the native_compile() takes care of destroying any previously
|
||||
// existing programs before it compiles.
|
||||
acquireReference();
|
||||
native_compile(sql);
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a NULL value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind null to
|
||||
*/
|
||||
public void bindNull(int index) {
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_null(index);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a long value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
public void bindLong(int index, long value) {
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_long(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a double value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
public void bindDouble(int index, double value) {
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_double(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a String value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
public void bindString(int index, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_string(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a byte array value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
public void bindBlob(int index, byte[] value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_blob(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all existing bindings. Unset bindings are treated as NULL.
|
||||
*/
|
||||
public void clearBindings() {
|
||||
acquireReference();
|
||||
try {
|
||||
native_clear_bindings();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release this program's resources, making it invalid.
|
||||
*/
|
||||
public void close() {
|
||||
mDatabase.lock();
|
||||
try {
|
||||
releaseReference();
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the native resource is cleaned up.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (nStatement != 0) {
|
||||
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
|
||||
String message = "Finalizing " + this +
|
||||
" that has not been closed";
|
||||
|
||||
Log.d(TAG, message + "\nThis cursor was created in:");
|
||||
for (StackTraceElement ste : mStackTraceElements) {
|
||||
Log.d(TAG, " " + ste);
|
||||
}
|
||||
}
|
||||
// when in finalize() it is already removed from weakhashmap
|
||||
// so it is safe to not removed itself from db
|
||||
onAllReferencesReleasedFromContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles SQL into a SQLite program.
|
||||
*
|
||||
* <P>The database lock must be held when calling this method.
|
||||
* @param sql The SQL to compile.
|
||||
*/
|
||||
protected final native void native_compile(String sql);
|
||||
protected final native void native_finalize();
|
||||
|
||||
protected final native void native_bind_null(int index);
|
||||
protected final native void native_bind_long(int index, long value);
|
||||
protected final native void native_bind_double(int index, double value);
|
||||
protected final native void native_bind_string(int index, String value);
|
||||
protected final native void native_bind_blob(int index, byte[] value);
|
||||
private final native void native_clear_bindings();
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.database.CursorWindow;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* A SQLite program that represents a query that reads the resulting rows into a CursorWindow.
|
||||
* This class is used by SQLiteCursor and isn't useful itself.
|
||||
*/
|
||||
public class SQLiteQuery extends SQLiteProgram {
|
||||
//private static final String TAG = "Cursor";
|
||||
|
||||
/** The index of the unbound OFFSET parameter */
|
||||
private int mOffsetIndex;
|
||||
|
||||
/** The SQL used to create this query */
|
||||
private String mQuery;
|
||||
|
||||
/** Args to bind on requery */
|
||||
private String[] mBindArgs;
|
||||
|
||||
private boolean mClosed = false;
|
||||
|
||||
/**
|
||||
* Create a persistent query object.
|
||||
*
|
||||
* @param db The database that this query object is associated with
|
||||
* @param query The SQL string for this query.
|
||||
* @param offsetIndex The 1-based index to the OFFSET parameter,
|
||||
*/
|
||||
/* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
|
||||
super(db, query);
|
||||
|
||||
mOffsetIndex = offsetIndex;
|
||||
mQuery = query;
|
||||
mBindArgs = bindArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads rows into a buffer. This method acquires the database lock.
|
||||
*
|
||||
* @param window The window to fill into
|
||||
* @return number of total rows in the query
|
||||
*/
|
||||
/* package */ int fillWindow(CursorWindow window,
|
||||
int maxRead, int lastPos) {
|
||||
mDatabase.lock();
|
||||
|
||||
boolean logStats = mDatabase.mLogStats;
|
||||
long startTime = logStats ? SystemClock.elapsedRealtime() : 0;
|
||||
try {
|
||||
acquireReference();
|
||||
try {
|
||||
window.acquireReference();
|
||||
// if the start pos is not equal to 0, then most likely window is
|
||||
// too small for the data set, loading by another thread
|
||||
// is not safe in this situation. the native code will ignore maxRead
|
||||
int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
|
||||
maxRead, lastPos);
|
||||
if (logStats) {
|
||||
mDatabase.logTimeStat(true /* read */, startTime,
|
||||
SystemClock.elapsedRealtime());
|
||||
}
|
||||
return numRows;
|
||||
} catch (IllegalStateException e){
|
||||
// simply ignore it
|
||||
return 0;
|
||||
} catch (SQLiteDatabaseCorruptException e) {
|
||||
mDatabase.onCorruption();
|
||||
throw e;
|
||||
} finally {
|
||||
window.releaseReference();
|
||||
}
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column count for the statement. Only valid on query based
|
||||
* statements. The database must be locked
|
||||
* when calling this method.
|
||||
*
|
||||
* @return The number of column in the statement's result set.
|
||||
*/
|
||||
/* package */ int columnCountLocked() {
|
||||
acquireReference();
|
||||
try {
|
||||
return native_column_count();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the column name for the given column index. The database must be locked
|
||||
* when calling this method.
|
||||
*
|
||||
* @param columnIndex the index of the column to get the name for
|
||||
* @return The requested column's name
|
||||
*/
|
||||
/* package */ String columnNameLocked(int columnIndex) {
|
||||
acquireReference();
|
||||
try {
|
||||
return native_column_name(columnIndex);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide pending API Council approval} */
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLiteQuery: " + mQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by SQLiteCursor when it is requeried.
|
||||
*/
|
||||
/* package */ void requery() {
|
||||
if (mBindArgs != null) {
|
||||
int len = mBindArgs.length;
|
||||
try {
|
||||
for (int i = 0; i < len; i++) {
|
||||
super.bindString(i + 1, mBindArgs[i]);
|
||||
}
|
||||
} catch (SQLiteMisuseException e) {
|
||||
StringBuilder errMsg = new StringBuilder("mQuery " + mQuery);
|
||||
for (int i = 0; i < len; i++) {
|
||||
errMsg.append(" ");
|
||||
errMsg.append(mBindArgs[i]);
|
||||
}
|
||||
errMsg.append(" ");
|
||||
IllegalStateException leakProgram = new IllegalStateException(
|
||||
errMsg.toString(), e);
|
||||
throw leakProgram;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(int index) {
|
||||
mBindArgs[index - 1] = null;
|
||||
if (!mClosed) super.bindNull(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindLong(int index, long value) {
|
||||
mBindArgs[index - 1] = Long.toString(value);
|
||||
if (!mClosed) super.bindLong(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindDouble(int index, double value) {
|
||||
mBindArgs[index - 1] = Double.toString(value);
|
||||
if (!mClosed) super.bindDouble(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindString(int index, String value) {
|
||||
mBindArgs[index - 1] = value;
|
||||
if (!mClosed) super.bindString(index, value);
|
||||
}
|
||||
|
||||
private final native int native_fill_window(CursorWindow window,
|
||||
int startPos, int offsetParam, int maxRead, int lastPos);
|
||||
|
||||
private final native int native_column_count();
|
||||
|
||||
private final native String native_column_name(int columnIndex);
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.provider.BaseColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* This is a convience class that helps build SQL queries to be sent to
|
||||
* {@link SQLiteDatabase} objects.
|
||||
*/
|
||||
public class SQLiteQueryBuilder
|
||||
{
|
||||
private static final String TAG = "SQLiteQueryBuilder";
|
||||
|
||||
private Map<String, String> mProjectionMap = null;
|
||||
private String mTables = "";
|
||||
private StringBuilder mWhereClause = new StringBuilder(64);
|
||||
private boolean mDistinct;
|
||||
private SQLiteDatabase.CursorFactory mFactory;
|
||||
|
||||
public SQLiteQueryBuilder() {
|
||||
mDistinct = false;
|
||||
mFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the query as DISTINCT.
|
||||
*
|
||||
* @param distinct if true the query is DISTINCT, otherwise it isn't
|
||||
*/
|
||||
public void setDistinct(boolean distinct) {
|
||||
mDistinct = distinct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of tables being queried
|
||||
*
|
||||
* @return the list of tables being queried
|
||||
*/
|
||||
public String getTables() {
|
||||
return mTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of tables to query. Multiple tables can be specified to perform a join.
|
||||
* For example:
|
||||
* setTables("foo, bar")
|
||||
* setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
|
||||
*
|
||||
* @param inTables the list of tables to query on
|
||||
*/
|
||||
public void setTables(String inTables) {
|
||||
mTables = inTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
||||
* WHERE clause looks like:
|
||||
*
|
||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
||||
*
|
||||
* @param inWhere the chunk of text to append to the WHERE clause.
|
||||
*/
|
||||
public void appendWhere(CharSequence inWhere) {
|
||||
if (mWhereClause.length() == 0) {
|
||||
mWhereClause.append('(');
|
||||
}
|
||||
mWhereClause.append(inWhere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
||||
* WHERE clause looks like:
|
||||
*
|
||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
||||
*
|
||||
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
|
||||
* to avoid SQL injection attacks
|
||||
*/
|
||||
public void appendWhereEscapeString(String inWhere) {
|
||||
if (mWhereClause.length() == 0) {
|
||||
mWhereClause.append('(');
|
||||
}
|
||||
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the projection map for the query. The projection map maps
|
||||
* from column names that the caller passes into query to database
|
||||
* column names. This is useful for renaming columns as well as
|
||||
* disambiguating column names when doing joins. For example you
|
||||
* could map "name" to "people.name". If a projection map is set
|
||||
* it must contain all column names the user may request, even if
|
||||
* the key and value are the same.
|
||||
*
|
||||
* @param columnMap maps from the user column names to the database column names
|
||||
*/
|
||||
public void setProjectionMap(Map<String, String> columnMap) {
|
||||
mProjectionMap = columnMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor factory to be used for the query. You can use
|
||||
* one factory for all queries on a database but it is normally
|
||||
* easier to specify the factory when doing this query. @param
|
||||
* factory the factor to use
|
||||
*/
|
||||
public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
|
||||
mFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an SQL query string from the given clauses.
|
||||
*
|
||||
* @param distinct true if you want each row to be unique, false otherwise.
|
||||
* @param tables The table names to compile the query against.
|
||||
* @param columns A list of which columns to return. Passing null will
|
||||
* return all columns, which is discouraged to prevent reading
|
||||
* data from storage that isn't going to be used.
|
||||
* @param where A filter declaring which rows to return, formatted as an SQL
|
||||
* WHERE clause (excluding the WHERE itself). Passing null will
|
||||
* return all rows for the given URL.
|
||||
* @param groupBy A filter declaring how to group rows, formatted as an SQL
|
||||
* GROUP BY clause (excluding the GROUP BY itself). Passing null
|
||||
* will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in the cursor,
|
||||
* if row grouping is being used, formatted as an SQL HAVING
|
||||
* clause (excluding the HAVING itself). Passing null will cause
|
||||
* all row groups to be included, and is required when row
|
||||
* grouping is not being used.
|
||||
* @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
|
||||
* (excluding the ORDER BY itself). Passing null will use the
|
||||
* default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return the SQL query string
|
||||
*/
|
||||
public static String buildQueryString(
|
||||
boolean distinct, String tables, String[] columns, String where,
|
||||
String groupBy, String having, String orderBy, String limit) {
|
||||
if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
|
||||
throw new IllegalArgumentException(
|
||||
"HAVING clauses are only permitted when using a groupBy clause");
|
||||
}
|
||||
|
||||
StringBuilder query = new StringBuilder(120);
|
||||
|
||||
query.append("SELECT ");
|
||||
if (distinct) {
|
||||
query.append("DISTINCT ");
|
||||
}
|
||||
if (columns != null && columns.length != 0) {
|
||||
appendColumns(query, columns);
|
||||
} else {
|
||||
query.append("* ");
|
||||
}
|
||||
query.append("FROM ");
|
||||
query.append(tables);
|
||||
appendClause(query, " WHERE ", where);
|
||||
appendClause(query, " GROUP BY ", groupBy);
|
||||
appendClause(query, " HAVING ", having);
|
||||
appendClause(query, " ORDER BY ", orderBy);
|
||||
appendClauseEscapeClause(query, " LIMIT ", limit);
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
private static void appendClause(StringBuilder s, String name, String clause) {
|
||||
if (!TextUtils.isEmpty(clause)) {
|
||||
s.append(name);
|
||||
s.append(clause);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendClauseEscapeClause(StringBuilder s, String name, String clause) {
|
||||
if (!TextUtils.isEmpty(clause)) {
|
||||
s.append(name);
|
||||
DatabaseUtils.appendEscapedSQLString(s, clause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the names that are non-null in columns to s, separating
|
||||
* them with commas.
|
||||
*/
|
||||
public static void appendColumns(StringBuilder s, String[] columns) {
|
||||
int n = columns.length;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String column = columns[i];
|
||||
|
||||
if (column != null) {
|
||||
if (i > 0) {
|
||||
s.append(", ");
|
||||
}
|
||||
s.append(column);
|
||||
}
|
||||
}
|
||||
s.append(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query by combining all current settings and the
|
||||
* information passed into this method.
|
||||
*
|
||||
* @param db the database to query on
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to prevent
|
||||
* reading data from storage that isn't going to be used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
||||
* itself). Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @return a cursor over the result set
|
||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
||||
* String, String[], String)
|
||||
*/
|
||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
||||
String selection, String[] selectionArgs, String groupBy,
|
||||
String having, String sortOrder) {
|
||||
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
|
||||
null /* limit */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query by combining all current settings and the
|
||||
* information passed into this method.
|
||||
*
|
||||
* @param db the database to query on
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to prevent
|
||||
* reading data from storage that isn't going to be used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
||||
* itself). Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return a cursor over the result set
|
||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
||||
* String, String[], String)
|
||||
*/
|
||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
||||
String selection, String[] selectionArgs, String groupBy,
|
||||
String having, String sortOrder, String limit) {
|
||||
if (mTables == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sql = buildQuery(
|
||||
projectionIn, selection, selectionArgs, groupBy, having,
|
||||
sortOrder, limit);
|
||||
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "Performing query: " + sql);
|
||||
}
|
||||
return db.rawQueryWithFactory(
|
||||
mFactory, sql, selectionArgs,
|
||||
SQLiteDatabase.findEditTable(mTables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SELECT statement suitable for use in a group of
|
||||
* SELECT statements that will be joined through UNION operators
|
||||
* in buildUnionQuery.
|
||||
*
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to
|
||||
* prevent reading data from storage that isn't going to be
|
||||
* used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given
|
||||
* URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
||||
* Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildQuery(
|
||||
String[] projectionIn, String selection, String[] selectionArgs,
|
||||
String groupBy, String having, String sortOrder, String limit) {
|
||||
String[] projection = computeProjection(projectionIn);
|
||||
|
||||
if (mWhereClause.length() > 0) {
|
||||
mWhereClause.append(')');
|
||||
}
|
||||
|
||||
// Tack on the user's selection, if present.
|
||||
if (selection != null && selection.length() > 0) {
|
||||
if (mWhereClause.length() > 0) {
|
||||
mWhereClause.append(" AND ");
|
||||
}
|
||||
|
||||
mWhereClause.append('(');
|
||||
mWhereClause.append(selection);
|
||||
mWhereClause.append(')');
|
||||
}
|
||||
|
||||
return buildQueryString(
|
||||
mDistinct, mTables, projection, mWhereClause.toString(),
|
||||
groupBy, having, sortOrder, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SELECT statement suitable for use in a group of
|
||||
* SELECT statements that will be joined through UNION operators
|
||||
* in buildUnionQuery.
|
||||
*
|
||||
* @param typeDiscriminatorColumn the name of the result column
|
||||
* whose cells will contain the name of the table from which
|
||||
* each row was drawn.
|
||||
* @param unionColumns the names of the columns to appear in the
|
||||
* result. This may include columns that do not appear in the
|
||||
* table this SELECT is querying (i.e. mTables), but that do
|
||||
* appear in one of the other tables in the UNION query that we
|
||||
* are constructing.
|
||||
* @param columnsPresentInTable a Set of the names of the columns
|
||||
* that appear in this table (i.e. in the table whose name is
|
||||
* mTables). Since columns in unionColumns include columns that
|
||||
* appear only in other tables, we use this array to distinguish
|
||||
* which ones actually are present. Other columns will have
|
||||
* NULL values for results from this subquery.
|
||||
* @param computedColumnsOffset all columns in unionColumns before
|
||||
* this index are included under the assumption that they're
|
||||
* computed and therefore won't appear in columnsPresentInTable,
|
||||
* e.g. "date * 1000 as normalized_date"
|
||||
* @param typeDiscriminatorValue the value used for the
|
||||
* type-discriminator column in this subquery
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given
|
||||
* URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
||||
* Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildUnionSubQuery(
|
||||
String typeDiscriminatorColumn,
|
||||
String[] unionColumns,
|
||||
Set<String> columnsPresentInTable,
|
||||
int computedColumnsOffset,
|
||||
String typeDiscriminatorValue,
|
||||
String selection,
|
||||
String[] selectionArgs,
|
||||
String groupBy,
|
||||
String having) {
|
||||
int unionColumnsCount = unionColumns.length;
|
||||
String[] projectionIn = new String[unionColumnsCount];
|
||||
|
||||
for (int i = 0; i < unionColumnsCount; i++) {
|
||||
String unionColumn = unionColumns[i];
|
||||
|
||||
if (unionColumn.equals(typeDiscriminatorColumn)) {
|
||||
projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
|
||||
+ typeDiscriminatorColumn;
|
||||
} else if (i <= computedColumnsOffset
|
||||
|| columnsPresentInTable.contains(unionColumn)) {
|
||||
projectionIn[i] = unionColumn;
|
||||
} else {
|
||||
projectionIn[i] = "NULL AS " + unionColumn;
|
||||
}
|
||||
}
|
||||
return buildQuery(
|
||||
projectionIn, selection, selectionArgs, groupBy, having,
|
||||
null /* sortOrder */,
|
||||
null /* limit */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of subqueries, all of which are SELECT statements,
|
||||
* construct a query that returns the union of what those
|
||||
* subqueries return.
|
||||
* @param subQueries an array of SQL SELECT statements, all of
|
||||
* which must have the same columns as the same positions in
|
||||
* their results
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing
|
||||
* null will use the default sort order, which may be unordered.
|
||||
* @param limit The limit clause, which applies to the entire union result set
|
||||
*
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
|
||||
StringBuilder query = new StringBuilder(128);
|
||||
int subQueryCount = subQueries.length;
|
||||
String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
|
||||
|
||||
for (int i = 0; i < subQueryCount; i++) {
|
||||
if (i > 0) {
|
||||
query.append(unionOperator);
|
||||
}
|
||||
query.append(subQueries[i]);
|
||||
}
|
||||
appendClause(query, " ORDER BY ", sortOrder);
|
||||
appendClause(query, " LIMIT ", limit);
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
private String[] computeProjection(String[] projectionIn) {
|
||||
if (projectionIn != null && projectionIn.length > 0) {
|
||||
if (mProjectionMap != null) {
|
||||
String[] projection = new String[projectionIn.length];
|
||||
int length = projectionIn.length;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
String userColumn = projectionIn[i];
|
||||
String column = mProjectionMap.get(userColumn);
|
||||
|
||||
if (column == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid column " + projectionIn[i]);
|
||||
} else {
|
||||
projection[i] = column;
|
||||
}
|
||||
}
|
||||
return projection;
|
||||
} else {
|
||||
return projectionIn;
|
||||
}
|
||||
} else if (mProjectionMap != null) {
|
||||
// Return all columns in projection map.
|
||||
Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
|
||||
String[] projection = new String[entrySet.size()];
|
||||
Iterator<Entry<String, String>> entryIter = entrySet.iterator();
|
||||
int i = 0;
|
||||
|
||||
while (entryIter.hasNext()) {
|
||||
Entry<String, String> entry = entryIter.next();
|
||||
|
||||
// Don't include the _count column when people ask for no projection.
|
||||
if (entry.getKey().equals(BaseColumns._COUNT)) {
|
||||
continue;
|
||||
}
|
||||
projection[i++] = entry.getValue();
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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 android.database.sqlite;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* A pre-compiled statement against a {@link SQLiteDatabase} that can be reused.
|
||||
* The statement cannot return multiple rows, but 1x1 result sets are allowed.
|
||||
* Don't use SQLiteStatement constructor directly, please use
|
||||
* {@link SQLiteDatabase#compileStatement(String)}
|
||||
*/
|
||||
public class SQLiteStatement extends SQLiteProgram
|
||||
{
|
||||
/**
|
||||
* Don't use SQLiteStatement constructor directly, please use
|
||||
* {@link SQLiteDatabase#compileStatement(String)}
|
||||
* @param db
|
||||
* @param sql
|
||||
*/
|
||||
/* package */ SQLiteStatement(SQLiteDatabase db, String sql) {
|
||||
super(db, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this SQL statement, if it is not a query. For example,
|
||||
* CREATE TABLE, DELTE, INSERT, etc.
|
||||
*
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
public void execute() {
|
||||
mDatabase.lock();
|
||||
boolean logStats = mDatabase.mLogStats;
|
||||
long startTime = logStats ? SystemClock.elapsedRealtime() : 0;
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
native_execute();
|
||||
if (logStats) {
|
||||
mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime());
|
||||
}
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this SQL statement and return the ID of the most
|
||||
* recently inserted row. The SQL statement should probably be an
|
||||
* INSERT for this to be a useful call.
|
||||
*
|
||||
* @return the row ID of the last row inserted.
|
||||
*
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
public long executeInsert() {
|
||||
mDatabase.lock();
|
||||
boolean logStats = mDatabase.mLogStats;
|
||||
long startTime = logStats ? SystemClock.elapsedRealtime() : 0;
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
native_execute();
|
||||
if (logStats) {
|
||||
mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime());
|
||||
}
|
||||
return mDatabase.lastInsertRow();
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a numeric value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
*
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
public long simpleQueryForLong() {
|
||||
mDatabase.lock();
|
||||
boolean logStats = mDatabase.mLogStats;
|
||||
long startTime = logStats ? SystemClock.elapsedRealtime() : 0;
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
long retValue = native_1x1_long();
|
||||
if (logStats) {
|
||||
mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime());
|
||||
}
|
||||
return retValue;
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a text value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
*
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
public String simpleQueryForString() {
|
||||
mDatabase.lock();
|
||||
boolean logStats = mDatabase.mLogStats;
|
||||
long startTime = logStats ? SystemClock.elapsedRealtime() : 0;
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
String retValue = native_1x1_string();
|
||||
if (logStats) {
|
||||
mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime());
|
||||
}
|
||||
return retValue;
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void native_execute();
|
||||
private final native long native_1x1_long();
|
||||
private final native String native_1x1_string();
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<HTML>
|
||||
<BODY>
|
||||
Contains the SQLite database management
|
||||
classes that an application would use to manage its own private database.
|
||||
<p>
|
||||
Applications use these classes to maange private databases. If creating a
|
||||
content provider, you will probably have to use these classes to create and
|
||||
manage your own database to store content. See <a
|
||||
href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> to learn
|
||||
the conventions for implementing a content provider. See the
|
||||
NotePadProvider class in the NotePad sample application in the SDK for an
|
||||
example of a content provider. Android ships with SQLite version 3.4.0
|
||||
<p>If you are working with data sent to you by a provider, you will not use
|
||||
these SQLite classes, but instead use the generic {@link android.database}
|
||||
classes.
|
||||
<p>Android ships with the sqlite3 database tool in the <code>tools/</code>
|
||||
folder. You can use this tool to browse or run SQL commands on the device. Run by
|
||||
typing <code>sqlite3</code> in a shell window.
|
||||
</BODY>
|
||||
</HTML>
|
||||
Reference in New Issue
Block a user