/* * 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 dalvik.system.CloseGuard; import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; import android.database.sqlite.SQLiteException; import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.util.Log; import android.util.SparseIntArray; /** * A buffer containing multiple cursor rows. *
* A {@link CursorWindow} is read-write when initially created and used locally. * When sent to a remote process (by writing it to a {@link Parcel}), the remote process * receives a read-only view of the cursor window. Typically the cursor window * will be allocated by the producer, filled with data, and then sent to the * consumer for reading. *
*/ public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; /** The cursor window size. resource xml file specifies the value in kB. * convert it to bytes here by multiplying with 1024. */ private static final int sCursorWindowSize = Resources.getSystem().getInteger( com.android.internal.R.integer.config_cursorWindowSize) * 1024; /** * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY) * @hide */ public int mWindowPtr; private int mStartPos; private final String mName; private final CloseGuard mCloseGuard = CloseGuard.get(); private static native int nativeCreate(String name, int cursorWindowSize); private static native int nativeCreateFromParcel(Parcel parcel); private static native void nativeDispose(int windowPtr); private static native void nativeWriteToParcel(int windowPtr, Parcel parcel); private static native void nativeClear(int windowPtr); private static native int nativeGetNumRows(int windowPtr); private static native boolean nativeSetNumColumns(int windowPtr, int columnNum); private static native boolean nativeAllocRow(int windowPtr); private static native void nativeFreeLastRow(int windowPtr); private static native int nativeGetType(int windowPtr, int row, int column); private static native byte[] nativeGetBlob(int windowPtr, int row, int column); private static native String nativeGetString(int windowPtr, int row, int column); private static native long nativeGetLong(int windowPtr, int row, int column); private static native double nativeGetDouble(int windowPtr, int row, int column); private static native void nativeCopyStringToBuffer(int windowPtr, int row, int column, CharArrayBuffer buffer); private static native boolean nativePutBlob(int windowPtr, byte[] value, int row, int column); private static native boolean nativePutString(int windowPtr, String value, int row, int column); private static native boolean nativePutLong(int windowPtr, long value, int row, int column); private static native boolean nativePutDouble(int windowPtr, double value, int row, int column); private static native boolean nativePutNull(int windowPtr, int row, int column); private static native String nativeGetName(int windowPtr); /** * Creates a new empty cursor window and gives it a name. ** The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to * set the number of columns before adding any rows to the cursor. *
* * @param name The name of the cursor window, or null if none. */ public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : "* The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to * set the number of columns before adding any rows to the cursor. *
* * @param localWindow True if this window will be used in this process only, * false if it might be sent to another processes. This argument is ignored. * * @deprecated There is no longer a distinction between local and remote * cursor windows. Use the {@link #CursorWindow(String)} constructor instead. */ @Deprecated public CursorWindow(boolean localWindow) { this((String)null); } private CursorWindow(Parcel source) { mStartPos = source.readInt(); mWindowPtr = nativeCreateFromParcel(source); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window could not be " + "created from binder."); } mName = nativeGetName(mWindowPtr); mCloseGuard.open("close"); } @Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } dispose(); } finally { super.finalize(); } } private void dispose() { if (mCloseGuard != null) { mCloseGuard.close(); } if (mWindowPtr != 0) { recordClosingOfWindow(mWindowPtr); nativeDispose(mWindowPtr); mWindowPtr = 0; } } /** * Gets the name of this cursor window, never null. * @hide */ public String getName() { return mName; } /** * Clears out the existing contents of the window, making it safe to reuse * for new data. ** The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}), * and number of columns in the cursor are all reset to zero. *
*/ public void clear() { acquireReference(); try { mStartPos = 0; nativeClear(mWindowPtr); } finally { releaseReference(); } } /** * Gets the start position of this cursor window. ** The start position is the zero-based index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. *
* * @return The zero-based start position. */ public int getStartPosition() { return mStartPos; } /** * Sets the start position of this cursor window. ** The start position is the zero-based index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. *
* * @param pos The new zero-based start position. */ public void setStartPosition(int pos) { mStartPos = pos; } /** * Gets the number of rows in this window. * * @return The number of rows in this cursor window. */ public int getNumRows() { acquireReference(); try { return nativeGetNumRows(mWindowPtr); } finally { releaseReference(); } } /** * Sets the number of columns in this window. ** This method must be called before any rows are added to the window, otherwise * it will fail to set the number of columns if it differs from the current number * of columns. *
* * @param columnNum The new number of columns. * @return True if successful. */ public boolean setNumColumns(int columnNum) { acquireReference(); try { return nativeSetNumColumns(mWindowPtr, columnNum); } finally { releaseReference(); } } /** * Allocates a new row at the end of this cursor window. * * @return True if successful, false if the cursor window is out of memory. */ public boolean allocRow(){ acquireReference(); try { return nativeAllocRow(mWindowPtr); } finally { releaseReference(); } } /** * Frees the last row in this cursor window. */ public void freeLastRow(){ acquireReference(); try { nativeFreeLastRow(mWindowPtr); } finally { releaseReference(); } } /** * Returns true if the field at the specified row and column index * has type {@link Cursor#FIELD_TYPE_NULL}. * * @param row The zero-based row index. * @param column The zero-based column index. * @return True if the field has type {@link Cursor#FIELD_TYPE_NULL}. * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated public boolean isNull(int row, int column) { return getType(row, column) == Cursor.FIELD_TYPE_NULL; } /** * Returns true if the field at the specified row and column index * has type {@link Cursor#FIELD_TYPE_BLOB} or {@link Cursor#FIELD_TYPE_NULL}. * * @param row The zero-based row index. * @param column The zero-based column index. * @return True if the field has type {@link Cursor#FIELD_TYPE_BLOB} or * {@link Cursor#FIELD_TYPE_NULL}. * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated public boolean isBlob(int row, int column) { int type = getType(row, column); return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL; } /** * Returns true if the field at the specified row and column index * has type {@link Cursor#FIELD_TYPE_INTEGER}. * * @param row The zero-based row index. * @param column The zero-based column index. * @return True if the field has type {@link Cursor#FIELD_TYPE_INTEGER}. * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated public boolean isLong(int row, int column) { return getType(row, column) == Cursor.FIELD_TYPE_INTEGER; } /** * Returns true if the field at the specified row and column index * has type {@link Cursor#FIELD_TYPE_FLOAT}. * * @param row The zero-based row index. * @param column The zero-based column index. * @return True if the field has type {@link Cursor#FIELD_TYPE_FLOAT}. * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated public boolean isFloat(int row, int column) { return getType(row, column) == Cursor.FIELD_TYPE_FLOAT; } /** * Returns true if the field at the specified row and column index * has type {@link Cursor#FIELD_TYPE_STRING} or {@link Cursor#FIELD_TYPE_NULL}. * * @param row The zero-based row index. * @param column The zero-based column index. * @return True if the field has type {@link Cursor#FIELD_TYPE_STRING} * or {@link Cursor#FIELD_TYPE_NULL}. * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated public boolean isString(int row, int column) { int type = getType(row, column); return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL; } /** * Returns the type of the field at the specified row and column index. ** The returned field types are: *
* The result is determined as follows: *
null
.* The result is determined as follows: *
null
.printf
family of functions using
* format specifier %lld
.printf
family of functions using
* format specifier %g
.* The buffer is populated as follows: *
printf
family of functions using
* format specifier %lld
.printf
family of functions using
* format specifier %g
.long
.
* * The result is determined as follows: *
0L
.strtoll
.
* long
value.long
.long
.
*/
public long getLong(int row, int column) {
acquireReference();
try {
return nativeGetLong(mWindowPtr, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Gets the value of the field at the specified row and column index as a
* double
.
* * The result is determined as follows: *
0.0
.strtod
.
* double
.double
value.double
.
*/
public double getDouble(int row, int column) {
acquireReference();
try {
return nativeGetDouble(mWindowPtr, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Gets the value of the field at the specified row and column index as a
* short
.
*
* The result is determined by invoking {@link #getLong} and converting the
* result to short
.
*
short
.
*/
public short getShort(int row, int column) {
return (short) getLong(row, column);
}
/**
* Gets the value of the field at the specified row and column index as an
* int
.
*
* The result is determined by invoking {@link #getLong} and converting the
* result to int
.
*
int
.
*/
public int getInt(int row, int column) {
return (int) getLong(row, column);
}
/**
* Gets the value of the field at the specified row and column index as a
* float
.
*
* The result is determined by invoking {@link #getDouble} and converting the
* result to float
.
*
float
.
*/
public float getFloat(int row, int column) {
return (float) getDouble(row, column);
}
/**
* Copies a byte array into the field at the specified row and column index.
*
* @param value The value to store.
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return True if successful.
*/
public boolean putBlob(byte[] value, int row, int column) {
acquireReference();
try {
return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Copies a string into the field at the specified row and column index.
*
* @param value The value to store.
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return True if successful.
*/
public boolean putString(String value, int row, int column) {
acquireReference();
try {
return nativePutString(mWindowPtr, value, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Puts a long integer into the field at the specified row and column index.
*
* @param value The value to store.
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return True if successful.
*/
public boolean putLong(long value, int row, int column) {
acquireReference();
try {
return nativePutLong(mWindowPtr, value, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Puts a double-precision floating point value into the field at the
* specified row and column index.
*
* @param value The value to store.
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return True if successful.
*/
public boolean putDouble(double value, int row, int column) {
acquireReference();
try {
return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
} finally {
releaseReference();
}
}
/**
* Puts a null value into the field at the specified row and column index.
*
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return True if successful.
*/
public boolean putNull(int row, int column) {
acquireReference();
try {
return nativePutNull(mWindowPtr, row - mStartPos, column);
} finally {
releaseReference();
}
}
public static final Parcelable.Creator